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().on_file_opened(&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::endpoints::RequestStream as _;
386 use fidl_fuchsia_ui_input::MediaButtonsEvent;
387 use fidl_fuchsia_ui_input3 as fuiinput;
388 use fidl_fuchsia_ui_pointer as fuipointer;
389 use fidl_fuchsia_ui_policy as fuipolicy;
390 use fuipointer::{
391 EventPhase, TouchEvent, TouchInteractionId, TouchPointerSample, TouchSourceV2Marker,
392 };
393 use futures::StreamExt as _;
394 use pretty_assertions::assert_eq;
395 use starnix_core::mm::{MemoryAccessor as _, MemoryAccessorExt as _};
396 use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
397 use starnix_core::task::{EventHandler, Waiter};
398 use starnix_core::testing::{map_memory, spawn_kernel_and_run};
399 use starnix_core::vfs::FileHandle;
400 use starnix_core::vfs::buffers::VecOutputBuffer;
401 use starnix_syscalls::SUCCESS;
402 use starnix_types::time::timeval_from_time;
403 use starnix_uapi::errors::EAGAIN;
404 use starnix_uapi::uapi;
405 use starnix_uapi::user_address::UserAddress;
406 use starnix_uapi::vfs::FdEvents;
407 use test_case::test_case;
408 use test_util::assert_near;
409 use zerocopy::FromBytes as _;
410
411 const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
412
413 async fn start_touch_input(
414 current_task: &CurrentTask,
415 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
416 let inspector = fuchsia_inspect::Inspector::default();
417 start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
418 }
419
420 async fn start_touch_input_inspect(
421 current_task: &CurrentTask,
422 inspector: &fuchsia_inspect::Inspector,
423 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
424 start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
425 }
426
427 async fn init_keyboard_listener(
428 keyboard_stream: &mut fuiinput::KeyboardRequestStream,
429 ) -> fuiinput::KeyboardListenerProxy {
430 let keyboard_listener = match keyboard_stream.next().await {
431 Some(Ok(fuiinput::KeyboardRequest::AddListener {
432 view_ref: _,
433 listener,
434 responder,
435 })) => {
436 let _ = responder.send();
437 listener.into_proxy()
438 }
439 _ => {
440 panic!("Failed to get event");
441 }
442 };
443
444 keyboard_listener
445 }
446
447 async fn init_button_listeners(
448 device_listener_stream: &mut fuipolicy::DeviceListenerRegistryRequestStream,
449 ) -> (fuipolicy::MediaButtonsListenerProxy, fuipolicy::TouchButtonsListenerProxy) {
450 let media_buttons_listener = match device_listener_stream.next().await {
451 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
452 listener,
453 responder,
454 })) => {
455 let _ = responder.send();
456 listener.into_proxy()
457 }
458 _ => {
459 panic!("Failed to get event");
460 }
461 };
462
463 let touch_buttons_listener = match device_listener_stream.next().await {
464 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
465 listener,
466 responder,
467 })) => {
468 let _ = responder.send();
469 listener.into_proxy()
470 }
471 _ => {
472 panic!("Failed to get event");
473 }
474 };
475
476 (media_buttons_listener, touch_buttons_listener)
477 }
478
479 async fn start_touch_input_inspect_and_dimensions(
480 current_task: &CurrentTask,
481 x_max: i32,
482 y_max: i32,
483 inspector: &fuchsia_inspect::Inspector,
484 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
485 let input_device = InputDevice::new_touch(x_max, y_max, inspector.root());
486 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
487
488 let (touch_source_client_end, touch_source_stream) =
489 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
490
491 let (mouse_source_client_end, _mouse_source_stream) =
492 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
493
494 let (keyboard_proxy, mut keyboard_stream) =
495 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
496 let view_ref_pair =
497 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
498
499 let (device_registry_proxy, mut device_listener_stream) =
500 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
501 );
502
503 let (relay, _relay_handle) = input_event_relay::new_input_relay();
504 relay.start_relays(
505 ¤t_task.kernel(),
506 EventProxyMode::None,
507 touch_source_client_end,
508 keyboard_proxy,
509 mouse_source_client_end,
510 view_ref_pair.view_ref,
511 device_registry_proxy,
512 input_device.open_files.clone(),
513 Default::default(),
514 None,
515 Some(input_device.inspect_status.clone()),
516 None,
517 );
518
519 let _ = init_keyboard_listener(&mut keyboard_stream).await;
520 let _ = init_button_listeners(&mut device_listener_stream).await;
521
522 (input_device, input_file, touch_source_stream)
523 }
524
525 async fn start_keyboard_input(
526 current_task: &CurrentTask,
527 ) -> (InputDevice, FileHandle, fuiinput::KeyboardListenerProxy) {
528 let inspector = fuchsia_inspect::Inspector::default();
529 start_keyboard_input_inspect(current_task, &inspector).await
530 }
531
532 async fn start_keyboard_input_inspect(
533 current_task: &CurrentTask,
534 inspector: &fuchsia_inspect::Inspector,
535 ) -> (InputDevice, FileHandle, fuiinput::KeyboardListenerProxy) {
536 let input_device = InputDevice::new_keyboard(inspector.root());
537 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
538 let (keyboard_proxy, mut keyboard_stream) =
539 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
540 let view_ref_pair =
541 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
542
543 let (device_registry_proxy, mut device_listener_stream) =
544 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
545 );
546
547 let (touch_source_client_end, _touch_source_stream) =
548 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
549
550 let (mouse_source_client_end, _mouse_source_stream) =
551 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
552
553 let (relay, _relay_handle) = input_event_relay::new_input_relay();
554 relay.start_relays(
555 current_task.kernel(),
556 EventProxyMode::None,
557 touch_source_client_end,
558 keyboard_proxy,
559 mouse_source_client_end,
560 view_ref_pair.view_ref,
561 device_registry_proxy,
562 Default::default(),
563 input_device.open_files.clone(),
564 None,
565 None,
566 Some(input_device.inspect_status.clone()),
567 );
568
569 let keyboad_listener = init_keyboard_listener(&mut keyboard_stream).await;
570 let _ = init_button_listeners(&mut device_listener_stream).await;
571
572 (input_device, input_file, keyboad_listener)
573 }
574
575 async fn start_button_input(
576 current_task: &CurrentTask,
577 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
578 let inspector = fuchsia_inspect::Inspector::default();
579 start_button_input_inspect(current_task, &inspector).await
580 }
581
582 async fn start_button_input_inspect(
583 current_task: &CurrentTask,
584 inspector: &fuchsia_inspect::Inspector,
585 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
586 let input_device = InputDevice::new_keyboard(inspector.root());
587 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
588 let (device_registry_proxy, mut device_listener_stream) =
589 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
590 );
591
592 let (touch_source_client_end, _touch_source_stream) =
593 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
594 let (mouse_source_client_end, _mouse_source_stream) =
595 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
596 let (keyboard_proxy, mut keyboard_stream) =
597 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
598 let view_ref_pair =
599 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
600
601 let (relay, _relay_handle) = input_event_relay::new_input_relay();
602 relay.start_relays(
603 current_task.kernel(),
604 EventProxyMode::None,
605 touch_source_client_end,
606 keyboard_proxy,
607 mouse_source_client_end,
608 view_ref_pair.view_ref,
609 device_registry_proxy,
610 Default::default(),
611 input_device.open_files.clone(),
612 None,
613 None,
614 Some(input_device.inspect_status.clone()),
615 );
616
617 let _ = init_keyboard_listener(&mut keyboard_stream).await;
618 let (button_listener, _) = init_button_listeners(&mut device_listener_stream).await;
619
620 (input_device, input_file, button_listener)
621 }
622
623 async fn start_mouse_input(
624 current_task: &CurrentTask,
625 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
626 let inspector = fuchsia_inspect::Inspector::default();
627 start_mouse_input_inspect(current_task, &inspector).await
628 }
629
630 async fn start_mouse_input_inspect(
631 current_task: &CurrentTask,
632 inspector: &fuchsia_inspect::Inspector,
633 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
634 let input_device = InputDevice::new_mouse(inspector.root());
635 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
636
637 let (touch_source_client_end, _touch_source_stream) =
638 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
639
640 let (mouse_source_client_end, mouse_source_stream) =
641 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
642
643 let (keyboard_proxy, mut keyboard_stream) =
644 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
645 let view_ref_pair =
646 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
647
648 let (device_registry_proxy, mut device_listener_stream) =
649 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
650 );
651
652 let (relay, _relay_handle) = input_event_relay::new_input_relay();
653 relay.start_relays(
654 ¤t_task.kernel(),
655 EventProxyMode::None,
656 touch_source_client_end,
657 keyboard_proxy,
658 mouse_source_client_end,
659 view_ref_pair.view_ref,
660 device_registry_proxy,
661 Default::default(),
662 Default::default(),
663 Some(input_device.clone()),
664 None,
665 None,
666 );
667
668 let _ = init_keyboard_listener(&mut keyboard_stream).await;
669 let _ = init_button_listeners(&mut device_listener_stream).await;
670
671 (input_device, input_file, mouse_source_stream)
672 }
673
674 fn make_touch_event(pointer_id: u32) -> fuipointer::TouchEvent {
675 make_touch_event_with_phase(EventPhase::Change, pointer_id)
677 }
678
679 fn make_touch_event_with_phase(phase: EventPhase, pointer_id: u32) -> fuipointer::TouchEvent {
680 make_touch_event_with_coords_phase(0.0, 0.0, phase, pointer_id)
681 }
682
683 fn make_touch_event_with_coords_phase(
684 x: f32,
685 y: f32,
686 phase: EventPhase,
687 pointer_id: u32,
688 ) -> fuipointer::TouchEvent {
689 make_touch_event_with_coords_phase_timestamp(x, y, phase, pointer_id, 0)
690 }
691
692 fn make_touch_event_with_coords(x: f32, y: f32, pointer_id: u32) -> fuipointer::TouchEvent {
693 make_touch_event_with_coords_phase(x, y, EventPhase::Change, pointer_id)
694 }
695
696 fn make_touch_event_with_coords_phase_timestamp(
697 x: f32,
698 y: f32,
699 phase: EventPhase,
700 pointer_id: u32,
701 time_nanos: i64,
702 ) -> fuipointer::TouchEvent {
703 make_touch_event_with_coords_phase_timestamp_device_id(
704 x, y, phase, pointer_id, time_nanos, 0,
705 )
706 }
707
708 fn make_empty_touch_event() -> fuipointer::TouchEvent {
709 TouchEvent {
710 pointer_sample: Some(TouchPointerSample {
711 interaction: Some(TouchInteractionId {
712 pointer_id: 0,
713 device_id: 0,
714 interaction_id: 0,
715 }),
716 ..Default::default()
717 }),
718 ..Default::default()
719 }
720 }
721
722 fn make_touch_event_with_coords_phase_timestamp_device_id(
723 x: f32,
724 y: f32,
725 phase: EventPhase,
726 pointer_id: u32,
727 time_nanos: i64,
728 device_id: u32,
729 ) -> fuipointer::TouchEvent {
730 TouchEvent {
731 timestamp: Some(time_nanos),
732 pointer_sample: Some(TouchPointerSample {
733 position_in_viewport: Some([x, y]),
734 phase: Some(phase),
736 interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
737 ..Default::default()
738 }),
739 ..Default::default()
740 }
741 }
742
743 fn make_mouse_wheel_event(ticks: i64) -> fuipointer::MouseEvent {
744 make_mouse_wheel_event_with_timestamp(ticks, 0)
745 }
746
747 fn make_mouse_wheel_event_with_timestamp(ticks: i64, timestamp: i64) -> fuipointer::MouseEvent {
748 fuipointer::MouseEvent {
749 timestamp: Some(timestamp),
750 pointer_sample: Some(fuipointer::MousePointerSample {
751 device_id: Some(0),
752 scroll_v: Some(ticks),
753 ..Default::default()
754 }),
755 ..Default::default()
756 }
757 }
758
759 fn read_uapi_events(file: &FileHandle, current_task: &CurrentTask) -> Vec<uapi::input_event> {
760 std::iter::from_fn(|| {
761 let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
762 match file.read(current_task, &mut event_bytes) {
763 Ok(INPUT_EVENT_SIZE) => Some(
764 uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
765 .map_err(|_| anyhow!("failed to read input_event from buffer")),
766 ),
767 Ok(other_size) => {
768 Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
769 }
770 Err(Errno { code: EAGAIN, .. }) => None,
771 Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
772 }
773 })
774 .enumerate()
775 .map(|(i, read_res)| match read_res {
776 Ok(event) => event,
777 Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
778 })
779 .collect()
780 }
781
782 async fn answer_next_touch_watch_request(
784 request_stream: &mut fuipointer::TouchSourceV2RequestStream,
785 touch_events: Vec<TouchEvent>,
786 ) {
787 let control_handle = request_stream.control_handle();
788 control_handle
789 .send_on_touch_events(touch_events, 1)
790 .expect("failure sending OnTouchEvents");
791 match request_stream.next().await {
792 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents { .. })) => {}
793 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
794 }
795 }
796
797 async fn answer_next_mouse_watch_request(
799 request_stream: &mut fuipointer::MouseSourceV2RequestStream,
800 mouse_events: Vec<fuipointer::MouseEvent>,
801 ) {
802 let control_handle = request_stream.control_handle();
803 control_handle
804 .send_on_mouse_events(mouse_events, 1)
805 .expect("failure sending OnMouseEvents");
806 match request_stream.next().await {
807 Some(Ok(fuipointer::MouseSourceV2Request::AcknowledgeEvents { .. })) => {}
808 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
809 }
810 }
811
812 #[::fuchsia::test]
813 async fn touch_source_v2_acknowledges_events_with_matching_stamp() {
814 spawn_kernel_and_run(async move |current_task| {
815 let (_input_device, _input_file, mut touch_source_stream) =
816 start_touch_input(¤t_task).await;
817
818 let control_handle = touch_source_stream.control_handle();
819 control_handle
820 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 10)
821 .expect("failure sending OnTouchEvents");
822 match touch_source_stream.next().await {
823 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
824 last_acknowledged_event_stamp,
825 ..
826 })) => assert_eq!(last_acknowledged_event_stamp, 10),
827 unexpected => panic!("unexpected request {:?}", unexpected),
828 }
829
830 control_handle
831 .send_on_touch_events(
832 vec![
833 make_empty_touch_event(),
834 make_empty_touch_event(),
835 make_empty_touch_event(),
836 make_empty_touch_event(),
837 make_empty_touch_event(),
838 ],
839 20,
840 )
841 .expect("failure sending OnTouchEvents");
842 match touch_source_stream.next().await {
843 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
844 last_acknowledged_event_stamp,
845 ..
846 })) => assert_eq!(last_acknowledged_event_stamp, 20),
847 unexpected => panic!("unexpected request {:?}", unexpected),
848 }
849 })
850 .await;
851 }
852
853 #[::fuchsia::test]
854 async fn notifies_polling_waiters_of_new_data() {
855 spawn_kernel_and_run(async move |current_task| {
856 let (_input_device, input_file, mut touch_source_stream) =
859 start_touch_input(¤t_task).await;
860 let waiter1 = Waiter::new();
861 let waiter2 = Waiter::new();
862
863 [&waiter1, &waiter2].iter().for_each(|waiter| {
865 input_file.wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None);
866 });
867 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
868 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
869
870 answer_next_touch_watch_request(
872 &mut touch_source_stream,
873 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
874 )
875 .await;
876 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
877 .await;
878
879 assert_eq!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
883 assert_eq!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
884 })
885 .await;
886 }
887
888 #[::fuchsia::test]
889 async fn notifies_blocked_waiter_of_new_data() {
890 spawn_kernel_and_run(async move |current_task| {
891 let kernel = current_task.kernel().clone();
893 let (_input_device, input_file, mut touch_source_stream) =
894 start_touch_input(¤t_task).await;
895 let waiter = Waiter::new();
896
897 input_file.wait_async(¤t_task, &waiter, FdEvents::POLLIN, EventHandler::None);
899
900 let closure = move |task: &CurrentTask| waiter.wait(&task);
901
902 let (waiter_thread, req) = SpawnRequestBuilder::new()
903 .with_debug_name("input-device-waiter")
904 .with_sync_closure(closure)
905 .build_with_async_result();
906 kernel.kthreads.spawner().spawn_from_request(req);
907
908 let mut waiter_thread = Box::pin(waiter_thread);
909 assert_matches!(futures::poll!(&mut waiter_thread), futures::task::Poll::Pending);
910
911 answer_next_touch_watch_request(
913 &mut touch_source_stream,
914 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
915 )
916 .await;
917
918 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
924 .await;
925 })
926 .await;
927 }
928
929 #[::fuchsia::test]
930 async fn does_not_notify_polling_waiters_without_new_data() {
931 spawn_kernel_and_run(async move |current_task| {
932 let (_input_device, input_file, mut touch_source_stream) =
935 start_touch_input(¤t_task).await;
936 let waiter1 = Waiter::new();
937 let waiter2 = Waiter::new();
938
939 [&waiter1, &waiter2].iter().for_each(|waiter| {
941 input_file.wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None);
942 });
943 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
944 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
945
946 answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
948
949 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
953 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
954 })
955 .await;
956 }
957
958 #[::fuchsia::test]
971 async fn honors_wait_cancellation() {
972 spawn_kernel_and_run(async move |current_task| {
973 let (_input_device, input_file, mut touch_source_stream) =
976 start_touch_input(¤t_task).await;
977 let waiter1 = Waiter::new();
978 let waiter2 = Waiter::new();
979
980 let waitkeys = [&waiter1, &waiter2]
982 .iter()
983 .map(|waiter| {
984 input_file
985 .wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None)
986 .expect("wait_async")
987 })
988 .collect::<Vec<_>>();
989
990 waitkeys.into_iter().next().expect("failed to get first waitkey").cancel();
992
993 answer_next_touch_watch_request(
995 &mut touch_source_stream,
996 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
997 )
998 .await;
999 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1001 .await;
1002
1003 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1007 assert_eq!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1008 })
1009 .await;
1010 }
1011
1012 #[::fuchsia::test]
1013 async fn query_events() {
1014 spawn_kernel_and_run(async move |current_task| {
1015 let (_input_device, input_file, mut touch_source_stream) =
1018 start_touch_input(¤t_task).await;
1019
1020 assert_eq!(
1022 input_file.query_events(¤t_task).expect("query_events"),
1023 FdEvents::empty(),
1024 "events should be empty before data arrives"
1025 );
1026
1027 answer_next_touch_watch_request(
1029 &mut touch_source_stream,
1030 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1031 )
1032 .await;
1033
1034 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1036 .await;
1037
1038 assert_eq!(
1040 input_file.query_events(¤t_task).expect("query_events"),
1041 FdEvents::POLLIN | FdEvents::POLLRDNORM,
1042 "events should be POLLIN after data arrives"
1043 );
1044 })
1045 .await;
1046 }
1047
1048 fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1049 make_uapi_input_event_with_timestamp(ty, code, value, 0)
1050 }
1051
1052 fn make_uapi_input_event_with_timestamp(
1053 ty: u32,
1054 code: u32,
1055 value: i32,
1056 time_nanos: i64,
1057 ) -> uapi::input_event {
1058 uapi::input_event {
1059 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
1060 type_: ty as u16,
1061 code: code as u16,
1062 value,
1063 }
1064 }
1065
1066 #[::fuchsia::test]
1067 async fn touch_event_ignored() {
1068 spawn_kernel_and_run(async move |current_task| {
1069 let inspector = fuchsia_inspect::Inspector::default();
1071
1072 let (_input_device, input_file, mut touch_source_stream) =
1073 start_touch_input_inspect(¤t_task, &inspector).await;
1074
1075 answer_next_touch_watch_request(
1078 &mut touch_source_stream,
1079 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1080 )
1081 .await;
1082
1083 answer_next_touch_watch_request(
1087 &mut touch_source_stream,
1088 vec![make_empty_touch_event()],
1089 )
1090 .await;
1091
1092 let events = read_uapi_events(&input_file, ¤t_task);
1094
1095 assert_eq!(events.len(), 6);
1096
1097 answer_next_touch_watch_request(
1100 &mut touch_source_stream,
1101 vec![make_empty_touch_event()],
1102 )
1103 .await;
1104
1105 let events = read_uapi_events(&input_file, ¤t_task);
1106 assert_eq!(events, vec![]);
1107 assert_data_tree!(inspector, root: {
1108 touch_device: {
1109 active_wake_leases_count: 0u64,
1110 total_events_with_wake_lease_count: 0u64,
1111 total_fidl_events_received_count: 3u64,
1112 total_fidl_events_ignored_count: 2u64,
1113 total_fidl_events_unexpected_count: 0u64,
1114 total_fidl_events_converted_count: 1u64,
1115 total_uapi_events_generated_count: 6u64,
1116 last_generated_uapi_event_timestamp_ns: 0i64,
1117 touch_file_0: {
1118 fidl_events_received_count: 3u64,
1119 fidl_events_ignored_count: 2u64,
1120 fidl_events_unexpected_count: 0u64,
1121 fidl_events_converted_count: 1u64,
1122 uapi_events_generated_count: 6u64,
1123 uapi_events_read_count: 6u64,
1124 fd_read_count: 8u64,
1125 fd_notify_count: 1u64,
1126 last_generated_uapi_event_timestamp_ns: 0i64,
1127 last_read_uapi_event_timestamp_ns: 0i64,
1128 opened_without_nonblock: AnyProperty,
1129 open_timestamp_ns: AnyProperty,
1130 closed: AnyProperty,
1131 close_timestamp_ns: AnyProperty,
1132 },
1133 }
1134 });
1135 })
1136 .await;
1137 }
1138
1139 #[test_case(make_touch_event_with_phase(EventPhase::Add, 1); "touch add for pointer already added")]
1140 #[test_case(make_touch_event_with_phase(EventPhase::Change, 2); "touch change for pointer not added")]
1141 #[test_case(make_touch_event_with_phase(EventPhase::Remove, 2); "touch remove for pointer not added")]
1142 #[test_case(make_touch_event_with_phase(EventPhase::Cancel, 1); "touch cancel")]
1143 #[::fuchsia::test]
1144 async fn touch_event_unexpected(event: TouchEvent) {
1145 spawn_kernel_and_run(async move |current_task| {
1146 let inspector = fuchsia_inspect::Inspector::default();
1148
1149 let (_input_device, input_file, mut touch_source_stream) =
1150 start_touch_input_inspect(¤t_task, &inspector).await;
1151
1152 answer_next_touch_watch_request(
1155 &mut touch_source_stream,
1156 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1157 )
1158 .await;
1159
1160 answer_next_touch_watch_request(
1164 &mut touch_source_stream,
1165 vec![make_empty_touch_event()],
1166 )
1167 .await;
1168
1169 let events = read_uapi_events(&input_file, ¤t_task);
1171
1172 assert_eq!(events.len(), 6);
1173
1174 answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1177
1178 let events = read_uapi_events(&input_file, ¤t_task);
1179 assert_eq!(events, vec![]);
1180 assert_data_tree!(inspector, root: {
1181 touch_device: {
1182 active_wake_leases_count: 0u64,
1183 total_events_with_wake_lease_count: 0u64,
1184 total_fidl_events_received_count: 3u64,
1185 total_fidl_events_ignored_count: 1u64,
1186 total_fidl_events_unexpected_count: 1u64,
1187 total_fidl_events_converted_count: 1u64,
1188 total_uapi_events_generated_count: 6u64,
1189 last_generated_uapi_event_timestamp_ns: 0i64,
1190 touch_file_0: {
1191 fidl_events_received_count: 3u64,
1192 fidl_events_ignored_count: 1u64,
1193 fidl_events_unexpected_count: 1u64,
1194 fidl_events_converted_count: 1u64,
1195 uapi_events_generated_count: 6u64,
1196 uapi_events_read_count: 6u64,
1197 fd_read_count: 8u64,
1198 fd_notify_count: 1u64,
1199 last_generated_uapi_event_timestamp_ns: 0i64,
1200 last_read_uapi_event_timestamp_ns: 0i64,
1201 opened_without_nonblock: AnyProperty,
1202 open_timestamp_ns: AnyProperty,
1203 closed: AnyProperty,
1204 close_timestamp_ns: AnyProperty,
1205 },
1206 }
1207 });
1208 })
1209 .await;
1210 }
1211
1212 #[::fuchsia::test]
1213 async fn translates_touch_add() {
1214 spawn_kernel_and_run(async move |current_task| {
1215 let (_input_device, input_file, mut touch_source_stream) =
1218 start_touch_input(¤t_task).await;
1219
1220 answer_next_touch_watch_request(
1222 &mut touch_source_stream,
1223 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1224 )
1225 .await;
1226
1227 answer_next_touch_watch_request(
1231 &mut touch_source_stream,
1232 vec![make_empty_touch_event()],
1233 )
1234 .await;
1235
1236 let events = read_uapi_events(&input_file, ¤t_task);
1238
1239 assert_eq!(
1240 events,
1241 vec![
1242 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1243 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1244 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1245 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1246 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1247 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1248 ]
1249 );
1250 })
1251 .await;
1252 }
1253
1254 #[::fuchsia::test]
1255 async fn translates_touch_change() {
1256 spawn_kernel_and_run(async move |current_task| {
1257 let (_input_device, input_file, mut touch_source_stream) =
1260 start_touch_input(¤t_task).await;
1261
1262 answer_next_touch_watch_request(
1264 &mut touch_source_stream,
1265 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1266 )
1267 .await;
1268
1269 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1273 .await;
1274
1275 let events = read_uapi_events(&input_file, ¤t_task);
1277
1278 assert_eq!(events.len(), 6);
1279
1280 answer_next_touch_watch_request(
1282 &mut touch_source_stream,
1283 vec![make_touch_event_with_coords(10.0, 20.0, 1)],
1284 )
1285 .await;
1286
1287 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1289 .await;
1290
1291 let events = read_uapi_events(&input_file, ¤t_task);
1292 assert_eq!(
1293 events,
1294 vec![
1295 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1296 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1297 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1298 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1299 ]
1300 );
1301 })
1302 .await;
1303 }
1304
1305 #[::fuchsia::test]
1306 async fn translates_touch_remove() {
1307 spawn_kernel_and_run(async move |current_task| {
1308 let (_input_device, input_file, mut touch_source_stream) =
1311 start_touch_input(¤t_task).await;
1312
1313 answer_next_touch_watch_request(
1315 &mut touch_source_stream,
1316 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1317 )
1318 .await;
1319
1320 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1324 .await;
1325
1326 let events = read_uapi_events(&input_file, ¤t_task);
1328
1329 assert_eq!(events.len(), 6);
1330
1331 answer_next_touch_watch_request(
1333 &mut touch_source_stream,
1334 vec![make_touch_event_with_phase(EventPhase::Remove, 1)],
1335 )
1336 .await;
1337
1338 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1340 .await;
1341
1342 let events = read_uapi_events(&input_file, ¤t_task);
1343 assert_eq!(
1344 events,
1345 vec![
1346 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1347 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1348 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1349 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1350 ]
1351 );
1352 })
1353 .await;
1354 }
1355
1356 #[::fuchsia::test]
1357 async fn multi_touch_event_sequence() {
1358 spawn_kernel_and_run(async move |current_task| {
1359 let (_input_device, input_file, mut touch_source_stream) =
1362 start_touch_input(¤t_task).await;
1363
1364 answer_next_touch_watch_request(
1366 &mut touch_source_stream,
1367 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1368 )
1369 .await;
1370
1371 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1373 .await;
1374 let events = read_uapi_events(&input_file, ¤t_task);
1375
1376 assert_eq!(events.len(), 6);
1377
1378 answer_next_touch_watch_request(
1380 &mut touch_source_stream,
1381 vec![
1382 make_touch_event_with_coords(10.0, 20.0, 1),
1383 make_touch_event_with_phase(EventPhase::Add, 2),
1384 ],
1385 )
1386 .await;
1387
1388 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1390 .await;
1391 let events = read_uapi_events(&input_file, ¤t_task);
1392
1393 assert_eq!(
1394 events,
1395 vec![
1396 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1397 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1398 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1399 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1400 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1401 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1402 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1403 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1404 ]
1405 );
1406
1407 answer_next_touch_watch_request(
1409 &mut touch_source_stream,
1410 vec![
1411 make_touch_event_with_coords(11.0, 21.0, 1),
1412 make_touch_event_with_coords(101.0, 201.0, 2),
1413 ],
1414 )
1415 .await;
1416
1417 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1419 .await;
1420 let events = read_uapi_events(&input_file, ¤t_task);
1421
1422 assert_eq!(
1423 events,
1424 vec![
1425 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1426 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
1427 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
1428 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1429 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
1430 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
1431 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1432 ]
1433 );
1434
1435 answer_next_touch_watch_request(
1437 &mut touch_source_stream,
1438 vec![
1439 make_touch_event_with_phase(EventPhase::Remove, 1),
1440 make_touch_event_with_coords(102.0, 202.0, 2),
1441 ],
1442 )
1443 .await;
1444
1445 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1447 .await;
1448 let events = read_uapi_events(&input_file, ¤t_task);
1449
1450 assert_eq!(
1451 events,
1452 vec![
1453 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1454 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1455 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1456 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 102),
1457 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 202),
1458 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1459 ]
1460 );
1461
1462 answer_next_touch_watch_request(
1464 &mut touch_source_stream,
1465 vec![make_touch_event_with_phase(EventPhase::Remove, 2)],
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(&input_file, ¤t_task);
1473
1474 assert_eq!(
1475 events,
1476 vec![
1477 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1478 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1479 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1480 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1481 ]
1482 );
1483 })
1484 .await;
1485 }
1486
1487 #[::fuchsia::test]
1488 async fn multi_event_sequence_unsorted_in_one_watch() {
1489 spawn_kernel_and_run(async move |current_task| {
1490 let (_input_device, input_file, mut touch_source_stream) =
1493 start_touch_input(¤t_task).await;
1494
1495 answer_next_touch_watch_request(
1497 &mut touch_source_stream,
1498 vec![
1499 make_touch_event_with_coords_phase_timestamp(
1500 10.0,
1501 20.0,
1502 EventPhase::Change,
1503 1,
1504 100,
1505 ),
1506 make_touch_event_with_coords_phase_timestamp(0.0, 0.0, EventPhase::Add, 1, 1),
1507 ],
1508 )
1509 .await;
1510
1511 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1513 .await;
1514 let events = read_uapi_events(&input_file, ¤t_task);
1515
1516 assert_eq!(
1517 events,
1518 vec![
1519 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
1520 make_uapi_input_event_with_timestamp(
1521 uapi::EV_ABS,
1522 uapi::ABS_MT_TRACKING_ID,
1523 1,
1524 1
1525 ),
1526 make_uapi_input_event_with_timestamp(
1527 uapi::EV_ABS,
1528 uapi::ABS_MT_POSITION_X,
1529 0,
1530 1
1531 ),
1532 make_uapi_input_event_with_timestamp(
1533 uapi::EV_ABS,
1534 uapi::ABS_MT_POSITION_Y,
1535 0,
1536 1
1537 ),
1538 make_uapi_input_event_with_timestamp(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
1539 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
1540 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 100),
1541 make_uapi_input_event_with_timestamp(
1542 uapi::EV_ABS,
1543 uapi::ABS_MT_POSITION_X,
1544 10,
1545 100
1546 ),
1547 make_uapi_input_event_with_timestamp(
1548 uapi::EV_ABS,
1549 uapi::ABS_MT_POSITION_Y,
1550 20,
1551 100
1552 ),
1553 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 100),
1554 ]
1555 );
1556 })
1557 .await;
1558 }
1559
1560 #[test_case((0.0, 0.0); "origin")]
1561 #[test_case((100.7, 200.7); "above midpoint")]
1562 #[test_case((100.3, 200.3); "below midpoint")]
1563 #[test_case((100.5, 200.5); "midpoint")]
1564 #[::fuchsia::test]
1565 async fn sends_acceptable_coordinates((x, y): (f32, f32)) {
1566 spawn_kernel_and_run(async move |current_task| {
1567 let (_input_device, input_file, mut touch_source_stream) =
1569 start_touch_input(¤t_task).await;
1570
1571 answer_next_touch_watch_request(
1573 &mut touch_source_stream,
1574 vec![make_touch_event_with_coords_phase(x, y, EventPhase::Add, 1)],
1575 )
1576 .await;
1577
1578 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1580 .await;
1581 let events = read_uapi_events(&input_file, ¤t_task);
1582
1583 const ACCEPTABLE_ERROR: f32 = 1.0;
1586 let actual_x = events
1587 .iter()
1588 .find(|event| {
1589 event.type_ == uapi::EV_ABS as u16
1590 && event.code == uapi::ABS_MT_POSITION_X as u16
1591 })
1592 .unwrap_or_else(|| panic!("did not find `ABS_X` event in {:?}", events))
1593 .value;
1594 let actual_y = events
1595 .iter()
1596 .find(|event| {
1597 event.type_ == uapi::EV_ABS as u16
1598 && event.code == uapi::ABS_MT_POSITION_Y as u16
1599 })
1600 .unwrap_or_else(|| panic!("did not find `ABS_Y` event in {:?}", events))
1601 .value;
1602 assert_near!(x, actual_x as f32, ACCEPTABLE_ERROR);
1603 assert_near!(y, actual_y as f32, ACCEPTABLE_ERROR);
1604 })
1605 .await;
1606 }
1607
1608 #[::fuchsia::test]
1609 async fn sends_appropriate_ack_to_touch_source_server() {
1610 spawn_kernel_and_run(async move |current_task| {
1611 let (_input_device, _input_file, mut touch_source_stream) =
1612 start_touch_input(¤t_task).await;
1613
1614 let control_handle = touch_source_stream.control_handle();
1615 control_handle
1616 .send_on_touch_events(vec![make_touch_event_with_phase(EventPhase::Add, 2)], 42)
1617 .expect("failure sending OnTouchEvents");
1618 match touch_source_stream.next().await {
1619 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
1620 last_acknowledged_event_stamp,
1621 ..
1622 })) => assert_eq!(last_acknowledged_event_stamp, 42),
1623 unexpected => panic!("unexpected request {:?}", unexpected),
1624 }
1625 })
1626 .await;
1627 }
1628
1629 #[test_case(fidl_fuchsia_input::Key::Escape, uapi::KEY_POWER; "Esc maps to Power")]
1630 #[test_case(fidl_fuchsia_input::Key::A, uapi::KEY_A; "A maps to A")]
1631 #[::fuchsia::test]
1632 async fn sends_keyboard_events(fkey: fidl_fuchsia_input::Key, lkey: u32) {
1633 spawn_kernel_and_run(async move |current_task| {
1634 let (_keyboard_device, keyboard_file, keyboard_listener) =
1635 start_keyboard_input(¤t_task).await;
1636
1637 let key_event = fuiinput::KeyEvent {
1638 timestamp: Some(0),
1639 type_: Some(fuiinput::KeyEventType::Pressed),
1640 key: Some(fkey),
1641 ..Default::default()
1642 };
1643
1644 let _ = keyboard_listener.on_key_event(&key_event).await;
1645 std::mem::drop(keyboard_listener); let events = read_uapi_events(&keyboard_file, ¤t_task);
1647 assert_eq!(events.len(), 2);
1648 assert_eq!(events[0].code, lkey as u16);
1649 })
1650 .await;
1651 }
1652
1653 #[::fuchsia::test]
1654 async fn skips_unknown_keyboard_events() {
1655 spawn_kernel_and_run(async move |current_task| {
1656 let inspector = fuchsia_inspect::Inspector::default();
1657 let (_keyboard_device, keyboard_file, keyboard_listener) =
1658 start_keyboard_input_inspect(¤t_task, &inspector).await;
1659
1660 let key_event = fuiinput::KeyEvent {
1661 timestamp: Some(0),
1662 type_: Some(fuiinput::KeyEventType::Pressed),
1663 key: Some(fidl_fuchsia_input::Key::AcRefresh),
1664 ..Default::default()
1665 };
1666
1667 let _ = keyboard_listener.on_key_event(&key_event).await;
1668 std::mem::drop(keyboard_listener); let events = read_uapi_events(&keyboard_file, ¤t_task);
1670 assert_eq!(events.len(), 0);
1671
1672 assert_data_tree!(inspector, root: {
1675 keyboard_device: {
1676 active_wake_leases_count: AnyProperty,
1677 total_events_with_wake_lease_count: AnyProperty,
1678 total_fidl_events_received_count: 1u64,
1679 total_fidl_events_ignored_count: 1u64,
1680 total_fidl_events_unexpected_count: 0u64,
1681 total_fidl_events_converted_count: 0u64,
1682 total_uapi_events_generated_count: 0u64,
1683 last_generated_uapi_event_timestamp_ns: 0i64,
1684 keyboard_file_0: {
1685 fidl_events_received_count: 1u64,
1686 fidl_events_ignored_count: 1u64,
1687 fidl_events_unexpected_count: 0u64,
1688 fidl_events_converted_count: 0u64,
1689 uapi_events_generated_count: 0u64,
1690 uapi_events_read_count: 0u64,
1691 fd_read_count: AnyProperty,
1692 fd_notify_count: AnyProperty,
1693 last_generated_uapi_event_timestamp_ns: 0i64,
1694 last_read_uapi_event_timestamp_ns: AnyProperty,
1695 opened_without_nonblock: AnyProperty,
1696 open_timestamp_ns: AnyProperty,
1697 closed: AnyProperty,
1698 close_timestamp_ns: AnyProperty,
1699 }
1700 }
1701 });
1702 })
1703 .await;
1704 }
1705
1706 #[::fuchsia::test]
1707 async fn sends_power_button_events() {
1708 spawn_kernel_and_run(async move |current_task| {
1709 let (_input_device, input_file, buttons_listener) =
1710 start_button_input(¤t_task).await;
1711
1712 let power_event = MediaButtonsEvent {
1713 volume: Some(0),
1714 mic_mute: Some(false),
1715 pause: Some(false),
1716 camera_disable: Some(false),
1717 power: Some(true),
1718 function: Some(false),
1719 ..Default::default()
1720 };
1721
1722 let _ = buttons_listener.on_event(power_event).await;
1723 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1726 assert_eq!(events.len(), 2);
1727 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1728 assert_eq!(events[0].value, 1);
1729 })
1730 .await;
1731 }
1732
1733 #[::fuchsia::test]
1734 async fn sends_function_button_events() {
1735 spawn_kernel_and_run(async move |current_task| {
1736 let (_input_device, input_file, buttons_listener) =
1737 start_button_input(¤t_task).await;
1738
1739 let function_event = MediaButtonsEvent {
1740 volume: Some(0),
1741 mic_mute: Some(false),
1742 pause: Some(false),
1743 camera_disable: Some(false),
1744 power: Some(false),
1745 function: Some(true),
1746 ..Default::default()
1747 };
1748
1749 let _ = buttons_listener.on_event(function_event).await;
1750 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1753 assert_eq!(events.len(), 2);
1754 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1755 assert_eq!(events[0].value, 1);
1756 })
1757 .await;
1758 }
1759
1760 #[::fuchsia::test]
1761 async fn sends_overlapping_button_events() {
1762 spawn_kernel_and_run(async move |current_task| {
1763 let (_input_device, input_file, buttons_listener) =
1764 start_button_input(¤t_task).await;
1765
1766 let power_event = MediaButtonsEvent {
1767 volume: Some(0),
1768 mic_mute: Some(false),
1769 pause: Some(false),
1770 camera_disable: Some(false),
1771 power: Some(true),
1772 function: Some(false),
1773 ..Default::default()
1774 };
1775
1776 let function_event = MediaButtonsEvent {
1777 volume: Some(0),
1778 mic_mute: Some(false),
1779 pause: Some(false),
1780 camera_disable: Some(false),
1781 power: Some(true),
1782 function: Some(true),
1783 ..Default::default()
1784 };
1785
1786 let function_release_event = MediaButtonsEvent {
1787 volume: Some(0),
1788 mic_mute: Some(false),
1789 pause: Some(false),
1790 camera_disable: Some(false),
1791 power: Some(true),
1792 function: Some(false),
1793 ..Default::default()
1794 };
1795
1796 let power_release_event = MediaButtonsEvent {
1797 volume: Some(0),
1798 mic_mute: Some(false),
1799 pause: Some(false),
1800 camera_disable: Some(false),
1801 power: Some(false),
1802 function: Some(false),
1803 ..Default::default()
1804 };
1805
1806 let _ = buttons_listener.on_event(power_event).await;
1807 let _ = buttons_listener.on_event(function_event).await;
1808 let _ = buttons_listener.on_event(function_release_event).await;
1809 let _ = buttons_listener.on_event(power_release_event).await;
1810 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1813 assert_eq!(events.len(), 8);
1814 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1815 assert_eq!(events[0].value, 1);
1816 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1817 assert_eq!(events[2].value, 1);
1818 assert_eq!(events[4].code, uapi::KEY_VOLUMEDOWN as u16);
1819 assert_eq!(events[4].value, 0);
1820 assert_eq!(events[6].code, uapi::KEY_POWER as u16);
1821 assert_eq!(events[6].value, 0);
1822 })
1823 .await;
1824 }
1825
1826 #[::fuchsia::test]
1827 async fn sends_simultaneous_button_events() {
1828 spawn_kernel_and_run(async move |current_task| {
1829 let (_input_device, input_file, buttons_listener) =
1830 start_button_input(¤t_task).await;
1831
1832 let power_and_function_event = MediaButtonsEvent {
1833 volume: Some(0),
1834 mic_mute: Some(false),
1835 pause: Some(false),
1836 camera_disable: Some(false),
1837 power: Some(true),
1838 function: Some(true),
1839 ..Default::default()
1840 };
1841
1842 let _ = buttons_listener.on_event(power_and_function_event).await;
1843 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1846 assert_eq!(events.len(), 4);
1847 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1848 assert_eq!(events[0].value, 1);
1849 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1850 assert_eq!(events[2].value, 1);
1851 })
1852 .await;
1853 }
1854
1855 #[::fuchsia::test]
1856 async fn sends_volume_up_button_events() {
1857 spawn_kernel_and_run(async move |current_task| {
1858 let (_input_device, input_file, buttons_listener) =
1859 start_button_input(¤t_task).await;
1860
1861 let volume_up_event = MediaButtonsEvent {
1862 volume: Some(1),
1863 mic_mute: Some(false),
1864 pause: Some(false),
1865 camera_disable: Some(false),
1866 power: Some(false),
1867 function: Some(false),
1868 ..Default::default()
1869 };
1870
1871 let _ = buttons_listener.on_event(volume_up_event).await;
1872 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1875 assert_eq!(events.len(), 2);
1876 assert_eq!(events[0].code, uapi::KEY_VOLUMEUP as u16);
1877 assert_eq!(events[0].value, 1);
1878 })
1879 .await;
1880 }
1881
1882 #[::fuchsia::test]
1883 async fn sends_volume_down_button_events() {
1884 spawn_kernel_and_run(async move |current_task| {
1885 let (_input_device, input_file, buttons_listener) =
1886 start_button_input(¤t_task).await;
1887
1888 let volume_down_event = MediaButtonsEvent {
1889 volume: Some(-1),
1890 mic_mute: Some(false),
1891 pause: Some(false),
1892 camera_disable: Some(false),
1893 power: Some(false),
1894 function: Some(false),
1895 ..Default::default()
1896 };
1897
1898 let _ = buttons_listener.on_event(volume_down_event).await;
1899 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1902 assert_eq!(events.len(), 2);
1903 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1904 assert_eq!(events[0].value, 1);
1905 })
1906 .await;
1907 }
1908
1909 #[test_case(1; "Scroll up")]
1910 #[test_case(-1; "Scroll down")]
1911 #[::fuchsia::test]
1912 async fn sends_mouse_wheel_events(ticks: i64) {
1913 spawn_kernel_and_run(async move |current_task| {
1914 let time = 100;
1915 let uapi_event = uapi::input_event {
1916 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time)),
1917 type_: uapi::EV_REL as u16,
1918 code: uapi::REL_WHEEL as u16,
1919 value: ticks as i32,
1920 };
1921
1922 let (_mouse_device, mouse_file, mut mouse_stream) =
1923 start_mouse_input(¤t_task).await;
1924
1925 answer_next_mouse_watch_request(
1926 &mut mouse_stream,
1927 vec![make_mouse_wheel_event_with_timestamp(ticks, time)],
1928 )
1929 .await;
1930
1931 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1934
1935 let events = read_uapi_events(&mouse_file, ¤t_task);
1936 assert_eq!(events.len(), 2);
1937 assert_eq!(events[0], uapi_event);
1938 })
1939 .await;
1940 }
1941
1942 #[::fuchsia::test]
1943 async fn handles_mouse_move_and_click_events() {
1944 spawn_kernel_and_run(async move |current_task| {
1945 let mouse_move_event = fuipointer::MouseEvent {
1946 timestamp: Some(0),
1947 pointer_sample: Some(fuipointer::MousePointerSample {
1948 device_id: Some(0),
1949 relative_motion: Some([10.0, 20.0]),
1950 ..Default::default()
1951 }),
1952 ..Default::default()
1953 };
1954 let mouse_click_event = fuipointer::MouseEvent {
1955 timestamp: Some(0),
1956 pointer_sample: Some(fuipointer::MousePointerSample {
1957 device_id: Some(0),
1958 pressed_buttons: Some(vec![1]),
1959 ..Default::default()
1960 }),
1961 ..Default::default()
1962 };
1963
1964 let (_mouse_device, mouse_file, mut mouse_stream) =
1965 start_mouse_input(¤t_task).await;
1966
1967 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_move_event]).await;
1969 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_click_event]).await;
1970
1971 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1974
1975 let events = read_uapi_events(&mouse_file, ¤t_task);
1976 assert!(!events.is_empty());
1977 assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
1978 && e.code == uapi::REL_X as u16
1979 && e.value == 10));
1980 assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
1981 && e.code == uapi::REL_Y as u16
1982 && e.value == 20));
1983 assert!(events.iter().any(|e| e.type_ == uapi::EV_KEY as u16
1984 && e.code == uapi::BTN_LEFT as u16
1985 && e.value == 1));
1986 })
1987 .await;
1988 }
1989
1990 #[::fuchsia::test]
1991 async fn touch_input_initialized_with_inspect_node() {
1992 spawn_kernel_and_run(async move |current_task| {
1993 let inspector = fuchsia_inspect::Inspector::default();
1994 let touch_device = InputDevice::new_touch(
1995 1200, 720, &inspector.root(),
1998 );
1999 let _file_obj = touch_device.open_test(¤t_task);
2000
2001 assert_data_tree!(inspector, root: {
2002 touch_device: {
2003 active_wake_leases_count: 0u64,
2004 total_events_with_wake_lease_count: 0u64,
2005 total_fidl_events_received_count: 0u64,
2006 total_fidl_events_ignored_count: 0u64,
2007 total_fidl_events_unexpected_count: 0u64,
2008 total_fidl_events_converted_count: 0u64,
2009 total_uapi_events_generated_count: 0u64,
2010 last_generated_uapi_event_timestamp_ns: 0i64,
2011 touch_file_0: {
2012 fidl_events_received_count: 0u64,
2013 fidl_events_ignored_count: 0u64,
2014 fidl_events_unexpected_count: 0u64,
2015 fidl_events_converted_count: 0u64,
2016 uapi_events_generated_count: 0u64,
2017 uapi_events_read_count: 0u64,
2018 fd_read_count: 0u64,
2019 fd_notify_count: 0u64,
2020 last_generated_uapi_event_timestamp_ns: 0i64,
2021 last_read_uapi_event_timestamp_ns: 0i64,
2022 opened_without_nonblock: AnyProperty,
2023 open_timestamp_ns: AnyProperty,
2024 closed: AnyProperty,
2025 close_timestamp_ns: AnyProperty,
2026 }
2027 }
2028 });
2029 })
2030 .await;
2031 }
2032
2033 #[::fuchsia::test]
2034 async fn touch_relay_updates_touch_inspect_status() {
2035 spawn_kernel_and_run(async move |current_task| {
2036 let inspector = fuchsia_inspect::Inspector::default();
2037
2038 let (_input_device, input_file, mut touch_source_stream) =
2039 start_touch_input_inspect(¤t_task, &inspector).await;
2040
2041 let control_handle = touch_source_stream.control_handle();
2044 control_handle
2045 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2046 .expect("failure sending OnTouchEvents");
2047 match touch_source_stream.next().await {
2048 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2049 last_acknowledged_event_stamp,
2050 ..
2051 })) => assert_eq!(last_acknowledged_event_stamp, 2),
2052 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2053 }
2054
2055 control_handle
2058 .send_on_touch_events(
2059 vec![
2060 make_touch_event_with_coords_phase_timestamp(
2061 0.0,
2062 0.0,
2063 EventPhase::Add,
2064 1,
2065 1000,
2066 ),
2067 make_touch_event_with_coords_phase_timestamp(
2068 1.0,
2069 1.0,
2070 EventPhase::Change,
2071 1,
2072 2000,
2073 ),
2074 make_touch_event_with_coords_phase_timestamp(
2075 2.0,
2076 2.0,
2077 EventPhase::Change,
2078 1,
2079 3000,
2080 ),
2081 make_touch_event_with_coords_phase_timestamp(
2082 3.0,
2083 3.0,
2084 EventPhase::Change,
2085 1,
2086 4000,
2087 ),
2088 make_touch_event_with_coords_phase_timestamp(
2089 3.0,
2090 3.0,
2091 EventPhase::Remove,
2092 1,
2093 5000,
2094 ),
2095 ],
2096 7,
2097 )
2098 .expect("failure sending OnTouchEvents");
2099 match touch_source_stream.next().await {
2100 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2101 last_acknowledged_event_stamp,
2102 ..
2103 })) => assert_eq!(last_acknowledged_event_stamp, 7),
2104 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2105 }
2106
2107 let _events = read_uapi_events(&input_file, ¤t_task);
2108 assert_data_tree!(inspector, root: {
2109 touch_device: {
2110 active_wake_leases_count: 0u64,
2111 total_events_with_wake_lease_count: 0u64,
2112 total_fidl_events_received_count: 7u64,
2113 total_fidl_events_ignored_count: 2u64,
2114 total_fidl_events_unexpected_count: 0u64,
2115 total_fidl_events_converted_count: 5u64,
2116 total_uapi_events_generated_count: 22u64,
2117 last_generated_uapi_event_timestamp_ns: 5000i64,
2118 touch_file_0: {
2119 fidl_events_received_count: 7u64,
2120 fidl_events_ignored_count: 2u64,
2121 fidl_events_unexpected_count: 0u64,
2122 fidl_events_converted_count: 5u64,
2123 uapi_events_generated_count: 22u64,
2124 uapi_events_read_count: 22u64,
2125 fd_read_count: 23u64,
2126 fd_notify_count: 1u64,
2127 last_generated_uapi_event_timestamp_ns: 5000i64,
2128 last_read_uapi_event_timestamp_ns: 5000i64,
2129 opened_without_nonblock: AnyProperty,
2130 open_timestamp_ns: AnyProperty,
2131 closed: AnyProperty,
2132 close_timestamp_ns: AnyProperty,
2133 },
2134 }
2135 });
2136 })
2137 .await;
2138 }
2139
2140 #[::fuchsia::test]
2141 async fn new_file_updates_inspect_status() {
2142 spawn_kernel_and_run(async move |current_task| {
2143 let inspector = fuchsia_inspect::Inspector::default();
2144
2145 let input_device = InputDevice::new_touch(700, 700, inspector.root());
2146 let input_file_0 =
2147 input_device.open_test(¤t_task).expect("Failed to create input file");
2148
2149 let (touch_source_client_end, mut touch_source_stream) =
2150 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
2151 let (mouse_source_client_end, _mouse_source_stream) =
2152 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
2153 let (keyboard_proxy, mut keyboard_stream) =
2154 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
2155 let view_ref_pair =
2156 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2157 let (device_registry_proxy, mut device_listener_stream) =
2158 fidl::endpoints::create_sync_proxy_and_stream::<
2159 fuipolicy::DeviceListenerRegistryMarker,
2160 >();
2161
2162 let (relay, relay_handle) = input_event_relay::new_input_relay();
2163 relay.start_relays(
2164 ¤t_task.kernel(),
2165 EventProxyMode::None,
2166 touch_source_client_end,
2167 keyboard_proxy,
2168 mouse_source_client_end,
2169 view_ref_pair.view_ref,
2170 device_registry_proxy,
2171 input_device.open_files.clone(),
2172 Default::default(),
2173 None,
2174 Some(input_device.inspect_status.clone()),
2175 None,
2176 );
2177
2178 let _ = init_keyboard_listener(&mut keyboard_stream).await;
2179 let _ = init_button_listeners(&mut device_listener_stream).await;
2180
2181 relay_handle.add_touch_device(
2182 0,
2183 input_device.open_files.clone(),
2184 Some(input_device.inspect_status.clone()),
2185 );
2186
2187 let control_handle = touch_source_stream.control_handle();
2190 control_handle
2191 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2192 .expect("failure sending OnTouchEvents");
2193 match touch_source_stream.next().await {
2194 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2195 last_acknowledged_event_stamp,
2196 ..
2197 })) => assert_eq!(last_acknowledged_event_stamp, 2),
2198 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2199 }
2200
2201 input_device.open_files.lock().clear();
2203 drop(input_file_0);
2204
2205 let input_file_1 =
2207 input_device.open_test(¤t_task).expect("Failed to create input file");
2208
2209 control_handle
2212 .send_on_touch_events(
2213 vec![
2214 make_touch_event_with_coords_phase_timestamp(
2215 0.0,
2216 0.0,
2217 EventPhase::Add,
2218 1,
2219 1000,
2220 ),
2221 make_touch_event_with_coords_phase_timestamp(
2222 1.0,
2223 1.0,
2224 EventPhase::Change,
2225 1,
2226 2000,
2227 ),
2228 make_touch_event_with_coords_phase_timestamp(
2229 2.0,
2230 2.0,
2231 EventPhase::Change,
2232 1,
2233 3000,
2234 ),
2235 make_touch_event_with_coords_phase_timestamp(
2236 3.0,
2237 3.0,
2238 EventPhase::Change,
2239 1,
2240 4000,
2241 ),
2242 make_touch_event_with_coords_phase_timestamp(
2243 3.0,
2244 3.0,
2245 EventPhase::Remove,
2246 1,
2247 5000,
2248 ),
2249 ],
2250 7,
2251 )
2252 .expect("failure sending OnTouchEvents");
2253 match touch_source_stream.next().await {
2254 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2255 last_acknowledged_event_stamp,
2256 ..
2257 })) => assert_eq!(last_acknowledged_event_stamp, 7),
2258 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2259 }
2260
2261 let _events = read_uapi_events(&input_file_1, ¤t_task);
2262
2263 input_device.open_files.lock().clear();
2265 drop(input_file_1);
2266
2267 assert_data_tree!(inspector, root: {
2268 touch_device: {
2269 active_wake_leases_count: 0u64,
2270 total_events_with_wake_lease_count: 0u64,
2271 total_fidl_events_received_count: 7u64,
2272 total_fidl_events_ignored_count: 2u64,
2273 total_fidl_events_unexpected_count: 0u64,
2274 total_fidl_events_converted_count: 5u64,
2275 total_uapi_events_generated_count: 22u64,
2276 last_generated_uapi_event_timestamp_ns: 5000i64,
2277 touch_file_0: {
2278 fidl_events_received_count: 2u64,
2279 fidl_events_ignored_count: 2u64,
2280 fidl_events_unexpected_count: 0u64,
2281 fidl_events_converted_count: 0u64,
2282 uapi_events_generated_count: 0u64,
2283 uapi_events_read_count: 0u64,
2284 fd_read_count: 0u64,
2285 fd_notify_count: 0u64,
2286 last_generated_uapi_event_timestamp_ns: 0i64,
2287 last_read_uapi_event_timestamp_ns: 0i64,
2288 opened_without_nonblock: AnyProperty,
2289 open_timestamp_ns: AnyProperty,
2290 closed: AnyProperty,
2291 close_timestamp_ns: AnyProperty,
2292 },
2293 touch_file_1: {
2294 fidl_events_received_count: 5u64,
2295 fidl_events_ignored_count: 0u64,
2296 fidl_events_unexpected_count: 0u64,
2297 fidl_events_converted_count: 5u64,
2298 uapi_events_generated_count: 22u64,
2299 uapi_events_read_count: 22u64,
2300 fd_read_count: 23u64,
2301 fd_notify_count: 1u64,
2302 last_generated_uapi_event_timestamp_ns: 5000i64,
2303 last_read_uapi_event_timestamp_ns: 5000i64,
2304 opened_without_nonblock: AnyProperty,
2305 open_timestamp_ns: AnyProperty,
2306 closed: AnyProperty,
2307 close_timestamp_ns: AnyProperty,
2308 },
2309 }
2310 });
2311 })
2312 .await;
2313 }
2314
2315 #[::fuchsia::test]
2316 async fn file_status_inspect_not_empty_after_close() {
2317 spawn_kernel_and_run(async move |current_task| {
2318 let inspector = fuchsia_inspect::Inspector::default();
2319
2320 let input_device = InputDevice::new_touch(700, 700, inspector.root());
2321 let file_handle =
2322 input_device.open_test(¤t_task).expect("Failed to create input file");
2323
2324 let status = file_handle
2325 .downcast_file::<crate::input_file::ArcInputFile>()
2326 .unwrap()
2327 .0
2328 .inspect_status
2329 .clone()
2330 .expect("touch file must have status");
2331 status.count_received_events(5);
2332
2333 assert_data_tree!(inspector, root: {
2334 touch_device: {
2335 active_wake_leases_count: AnyProperty,
2336 last_generated_uapi_event_timestamp_ns: AnyProperty,
2337 total_events_with_wake_lease_count: AnyProperty,
2338 total_fidl_events_converted_count: AnyProperty,
2339 total_fidl_events_ignored_count: AnyProperty,
2340 total_fidl_events_received_count: AnyProperty,
2341 total_fidl_events_unexpected_count: AnyProperty,
2342 total_uapi_events_generated_count: AnyProperty,
2343 touch_file_0: {
2344 fidl_events_received_count: 5u64,
2345 fd_notify_count: 0u64,
2346 fd_read_count: 0u64,
2347 fidl_events_converted_count: 0u64,
2348 fidl_events_ignored_count: 0u64,
2349 fidl_events_unexpected_count: 0u64,
2350 last_generated_uapi_event_timestamp_ns: 0i64,
2351 last_read_uapi_event_timestamp_ns: 0i64,
2352 uapi_events_generated_count: 0u64,
2353 uapi_events_read_count: 0u64,
2354 opened_without_nonblock: true,
2355 open_timestamp_ns: AnyProperty,
2356 closed: false,
2357 close_timestamp_ns: 0i64,
2358 }
2359 }
2360 });
2361
2362 drop(status);
2363 input_device.open_files.lock().clear();
2364 drop(file_handle);
2365
2366 assert_data_tree!(inspector, root: {
2367 touch_device: {
2368 active_wake_leases_count: AnyProperty,
2369 last_generated_uapi_event_timestamp_ns: AnyProperty,
2370 total_events_with_wake_lease_count: AnyProperty,
2371 total_fidl_events_converted_count: AnyProperty,
2372 total_fidl_events_ignored_count: AnyProperty,
2373 total_fidl_events_received_count: AnyProperty,
2374 total_fidl_events_unexpected_count: AnyProperty,
2375 total_uapi_events_generated_count: AnyProperty,
2376 touch_file_0: {
2377 fidl_events_received_count: 5u64,
2378 fd_notify_count: 0u64,
2379 fd_read_count: 0u64,
2380 fidl_events_converted_count: 0u64,
2381 fidl_events_ignored_count: 0u64,
2382 fidl_events_unexpected_count: 0u64,
2383 last_generated_uapi_event_timestamp_ns: 0i64,
2384 last_read_uapi_event_timestamp_ns: 0i64,
2385 uapi_events_generated_count: 0u64,
2386 uapi_events_read_count: 0u64,
2387 opened_without_nonblock: true,
2388 open_timestamp_ns: AnyProperty,
2389 closed: AnyProperty,
2390 close_timestamp_ns: AnyProperty,
2391 }
2392 }
2393 });
2394 })
2395 .await;
2396 }
2397
2398 #[::fuchsia::test]
2399 async fn keyboard_input_initialized_with_inspect_node() {
2400 spawn_kernel_and_run(async move |current_task| {
2401 let inspector = fuchsia_inspect::Inspector::default();
2402 let keyboard_device = InputDevice::new_keyboard(&inspector.root());
2403 let _file_obj = keyboard_device.open_test(¤t_task);
2404
2405 assert_data_tree!(inspector, root: {
2406 keyboard_device: {
2407 active_wake_leases_count: 0u64,
2408 total_events_with_wake_lease_count: 0u64,
2409 total_fidl_events_received_count: 0u64,
2410 total_fidl_events_ignored_count: 0u64,
2411 total_fidl_events_unexpected_count: 0u64,
2412 total_fidl_events_converted_count: 0u64,
2413 total_uapi_events_generated_count: 0u64,
2414 last_generated_uapi_event_timestamp_ns: 0i64,
2415 keyboard_file_0: {
2416 fidl_events_received_count: 0u64,
2417 fidl_events_ignored_count: 0u64,
2418 fidl_events_unexpected_count: 0u64,
2419 fidl_events_converted_count: 0u64,
2420 uapi_events_generated_count: 0u64,
2421 uapi_events_read_count: 0u64,
2422 fd_read_count: 0u64,
2423 fd_notify_count: 0u64,
2424 last_generated_uapi_event_timestamp_ns: 0i64,
2425 last_read_uapi_event_timestamp_ns: 0i64,
2426 opened_without_nonblock: AnyProperty,
2427 open_timestamp_ns: AnyProperty,
2428 closed: AnyProperty,
2429 close_timestamp_ns: AnyProperty,
2430 }
2431 }
2432 });
2433 })
2434 .await;
2435 }
2436
2437 #[::fuchsia::test]
2438 async fn button_relay_updates_keyboard_inspect_status() {
2439 spawn_kernel_and_run(async move |current_task| {
2440 let inspector = fuchsia_inspect::Inspector::default();
2441
2442 let (_input_device, input_file, buttons_listener) =
2443 start_button_input_inspect(¤t_task, &inspector).await;
2444
2445 let power_event = MediaButtonsEvent {
2448 volume: Some(0),
2449 mic_mute: Some(false),
2450 pause: Some(false),
2451 camera_disable: Some(false),
2452 power: Some(true),
2453 function: Some(false),
2454 ..Default::default()
2455 };
2456
2457 let power_release_event = MediaButtonsEvent {
2458 volume: Some(0),
2459 mic_mute: Some(false),
2460 pause: Some(false),
2461 camera_disable: Some(false),
2462 power: Some(false),
2463 function: Some(false),
2464 ..Default::default()
2465 };
2466
2467 let _ = buttons_listener.on_event(power_event).await;
2468 let _ = buttons_listener.on_event(power_release_event).await;
2469
2470 let events = read_uapi_events(&input_file, ¤t_task);
2471 assert_eq!(events.len(), 4);
2472 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2473 assert_eq!(events[0].value, 1);
2474 assert_eq!(events[2].code, uapi::KEY_POWER as u16);
2475 assert_eq!(events[2].value, 0);
2476
2477 let _events = read_uapi_events(&input_file, ¤t_task);
2478
2479 assert_data_tree!(inspector, root: {
2480 keyboard_device: {
2481 active_wake_leases_count: 0u64,
2482 total_events_with_wake_lease_count: 0u64,
2483 total_fidl_events_received_count: 2u64,
2484 total_fidl_events_ignored_count: 0u64,
2485 total_fidl_events_unexpected_count: 0u64,
2486 total_fidl_events_converted_count: 2u64,
2487 total_uapi_events_generated_count: 4u64,
2488 last_generated_uapi_event_timestamp_ns: AnyProperty,
2490 keyboard_file_0: {
2491 fidl_events_received_count: 2u64,
2492 fidl_events_ignored_count: 0u64,
2493 fidl_events_unexpected_count: 0u64,
2494 fidl_events_converted_count: 2u64,
2495 uapi_events_generated_count: 4u64,
2496 uapi_events_read_count: 4u64,
2497 fd_read_count: 6u64,
2498 fd_notify_count: 2u64,
2499 last_generated_uapi_event_timestamp_ns: AnyProperty,
2501 last_read_uapi_event_timestamp_ns: AnyProperty,
2502 opened_without_nonblock: AnyProperty,
2503 open_timestamp_ns: AnyProperty,
2504 closed: AnyProperty,
2505 close_timestamp_ns: AnyProperty,
2506 },
2507 }
2508 });
2509 })
2510 .await;
2511 }
2512
2513 #[::fuchsia::test]
2514 async fn mouse_input_initialized_with_inspect_node() {
2515 spawn_kernel_and_run(async move |current_task| {
2516 let inspector = fuchsia_inspect::Inspector::default();
2517 let mouse_device = InputDevice::new_mouse(&inspector.root());
2518 let _file_obj = mouse_device.open_test(¤t_task);
2519
2520 assert_data_tree!(inspector, root: {
2521 mouse_device: {
2522 active_wake_leases_count: 0u64,
2523 total_events_with_wake_lease_count: 0u64,
2524 total_fidl_events_received_count: 0u64,
2525 total_fidl_events_ignored_count: 0u64,
2526 total_fidl_events_unexpected_count: 0u64,
2527 total_fidl_events_converted_count: 0u64,
2528 total_uapi_events_generated_count: 0u64,
2529 last_generated_uapi_event_timestamp_ns: 0i64,
2530 mouse_file_0: {
2531 fidl_events_received_count: 0u64,
2532 fidl_events_ignored_count: 0u64,
2533 fidl_events_unexpected_count: 0u64,
2534 fidl_events_converted_count: 0u64,
2535 uapi_events_generated_count: 0u64,
2536 uapi_events_read_count: 0u64,
2537 fd_read_count: 0u64,
2538 fd_notify_count: 0u64,
2539 last_generated_uapi_event_timestamp_ns: 0i64,
2540 last_read_uapi_event_timestamp_ns: 0i64,
2541 opened_without_nonblock: AnyProperty,
2542 open_timestamp_ns: AnyProperty,
2543 closed: AnyProperty,
2544 close_timestamp_ns: AnyProperty,
2545 }
2546 }
2547 });
2548 })
2549 .await;
2550 }
2551
2552 #[::fuchsia::test]
2553 async fn mouse_relay_updates_mouse_inspect_status() {
2554 spawn_kernel_and_run(async move |current_task| {
2555 let inspector = fuchsia_inspect::Inspector::default();
2556
2557 let (_input_device, input_file, mut mouse_source_stream) =
2558 start_mouse_input_inspect(¤t_task, &inspector).await;
2559
2560 let mouse_move_event = fuipointer::MouseEvent {
2561 timestamp: Some(0),
2562 pointer_sample: Some(fuipointer::MousePointerSample {
2563 device_id: Some(0),
2564 position_in_viewport: Some([50.0, 50.0]),
2565 scroll_v: Some(0),
2566 ..Default::default()
2567 }),
2568 ..Default::default()
2569 };
2570 let mouse_click_event = fuipointer::MouseEvent {
2571 timestamp: Some(0),
2572 pointer_sample: Some(fuipointer::MousePointerSample {
2573 device_id: Some(0),
2574 scroll_v: Some(0),
2575 pressed_buttons: Some(vec![1]),
2576 ..Default::default()
2577 }),
2578 ..Default::default()
2579 };
2580
2581 answer_next_mouse_watch_request(
2584 &mut mouse_source_stream,
2585 vec![mouse_move_event, mouse_click_event],
2586 )
2587 .await;
2588
2589 answer_next_mouse_watch_request(
2592 &mut mouse_source_stream,
2593 (0..5).map(|_| make_mouse_wheel_event(1)).collect(),
2594 )
2595 .await;
2596
2597 answer_next_mouse_watch_request(
2600 &mut mouse_source_stream,
2601 vec![make_mouse_wheel_event_with_timestamp(-1, 5000)],
2602 )
2603 .await;
2604
2605 answer_next_mouse_watch_request(&mut mouse_source_stream, vec![]).await;
2608
2609 let _events = read_uapi_events(&input_file, ¤t_task);
2610 assert_data_tree!(inspector, root: {
2611 mouse_device: {
2612 active_wake_leases_count: 0u64,
2613 total_events_with_wake_lease_count: 0u64,
2614 total_fidl_events_received_count: 8u64,
2615 total_fidl_events_ignored_count: 1u64,
2616 total_fidl_events_unexpected_count: 0u64,
2617 total_fidl_events_converted_count: 7u64,
2618 total_uapi_events_generated_count: 7u64,
2619 last_generated_uapi_event_timestamp_ns: 5000i64,
2620 mouse_file_0: {
2621 fidl_events_received_count: 8u64,
2622 fidl_events_ignored_count: 1u64,
2623 fidl_events_unexpected_count: 0u64,
2624 fidl_events_converted_count: 7u64,
2625 uapi_events_generated_count: 7u64,
2626 uapi_events_read_count: 7u64,
2627 fd_read_count: 8u64,
2628 fd_notify_count: 3u64,
2629 last_generated_uapi_event_timestamp_ns: 5000i64,
2630 last_read_uapi_event_timestamp_ns: 5000i64,
2631 opened_without_nonblock: AnyProperty,
2632 open_timestamp_ns: AnyProperty,
2633 closed: AnyProperty,
2634 close_timestamp_ns: AnyProperty,
2635 },
2636 }
2637 });
2638 })
2639 .await;
2640 }
2641
2642 #[::fuchsia::test]
2643 async fn eviocgname_zero_buffer_length_succeeds() {
2644 spawn_kernel_and_run(async move |current_task| {
2645 let inspector = fuchsia_inspect::Inspector::default();
2646 let touch_device = InputDevice::new_touch(700, 1200, &inspector.root());
2647 let file =
2648 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2649
2650 let user_addr = starnix_core::testing::map_memory(
2651 ¤t_task,
2652 starnix_uapi::user_address::UserAddress::default(),
2653 4096,
2654 );
2655
2656 let res = file.ioctl(¤t_task, uapi::EVIOCGNAME_0, user_addr.into());
2658 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2659
2660 let name_req = uapi::EVIOCGNAME_0 | (256 << 16);
2662 let res = file.ioctl(¤t_task, name_req, user_addr.into());
2663 assert!(res.is_ok());
2664 let bytes_written = res.unwrap().value() as usize;
2665 assert!(bytes_written > 0);
2666 })
2667 .await;
2668 }
2669
2670 #[::fuchsia::test]
2671 async fn evdev_variable_length_ioctls_succeed() {
2672 spawn_kernel_and_run(async move |current_task| {
2673 let inspector = fuchsia_inspect::Inspector::default();
2674 let touch_device = InputDevice::new_touch(700, 1200, &inspector.root());
2675 let file =
2676 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2677
2678 let user_addr = starnix_core::testing::map_memory(
2679 ¤t_task,
2680 starnix_uapi::user_address::UserAddress::default(),
2681 4096,
2682 );
2683
2684 let res = file.ioctl(¤t_task, uapi::EVIOCGRAB, 1u64.into());
2686 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2687
2688 let phys_req = crate::input_file::EVIOCGPHYS_BASE | (256 << 16);
2690 let res = file.ioctl(¤t_task, phys_req, user_addr.into());
2691 assert!(res.is_ok());
2692 let bytes_written = res.unwrap().value() as usize;
2693 assert!(bytes_written > 0);
2694
2695 let uniq_req = crate::input_file::EVIOCGUNIQ_BASE | (256 << 16);
2697 let res = file.ioctl(¤t_task, uniq_req, user_addr.into());
2698 assert_eq!(res, starnix_uapi::error!(ENOENT));
2699
2700 let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
2702 let res = file.ioctl(¤t_task, prop_req, user_addr.into());
2703 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2704
2705 let bit0_req = crate::input_file::EVIOCGBIT_0_BASE | (64 << 16);
2707 let res = file.ioctl(¤t_task, bit0_req, user_addr.into());
2708 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2709
2710 let snd_req = crate::input_file::EVIOCGBIT_EV_SND_BASE | (64 << 16);
2712 let res = file.ioctl(¤t_task, snd_req, user_addr.into());
2713 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2714
2715 let invalid_bit_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
2717 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2718 | (64 << 16)
2719 | (0x20 + 0x1f);
2720 let res = file.ioctl(¤t_task, invalid_bit_req, user_addr.into());
2721 assert_eq!(res, starnix_uapi::error!(EINVAL));
2722
2723 let unrecognized_evdev_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
2725 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2726 | 0xFF;
2727 let res = file.ioctl(¤t_task, unrecognized_evdev_req, user_addr.into());
2728 assert_eq!(res, starnix_uapi::error!(EINVAL));
2729
2730 let invalid_dir_req = (uapi::_IOC_WRITE << uapi::_IOC_DIRSHIFT)
2732 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2733 | 0x20;
2734 let res = file.ioctl(¤t_task, invalid_dir_req, user_addr.into());
2735 assert_eq!(res, starnix_uapi::error!(EINVAL));
2736 })
2737 .await;
2738 }
2739
2740 #[::fuchsia::test]
2746 async fn evdev_current_state_queries_succeed() {
2747 spawn_kernel_and_run(async move |current_task| {
2748 let inspector = fuchsia_inspect::Inspector::default();
2749 let touch_device = InputDevice::new_touch(700, 1200, &inspector.root());
2750 let file =
2751 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2752
2753 let user_addr = starnix_core::testing::map_memory(
2754 ¤t_task,
2755 starnix_uapi::user_address::UserAddress::default(),
2756 4096,
2757 );
2758
2759 const BUF_LEN: usize = 96;
2760 for (name, base) in [
2761 ("EVIOCGKEY", crate::input_file::EVIOCGKEY_BASE),
2762 ("EVIOCGLED", crate::input_file::EVIOCGLED_BASE),
2763 ("EVIOCGSND", crate::input_file::EVIOCGSND_BASE),
2764 ("EVIOCGSW", crate::input_file::EVIOCGSW_BASE),
2765 ] {
2766 current_task
2769 .write_memory(user_addr, &[0xAAu8; BUF_LEN])
2770 .expect("failed to poison buffer");
2771
2772 let req = base | ((BUF_LEN as u32) << 16);
2773 let res = file.ioctl(¤t_task, req, user_addr.into());
2774 assert_eq!(res, Ok(starnix_syscalls::SUCCESS), "{name} should succeed");
2775
2776 let read_back = current_task
2777 .read_memory_to_vec(user_addr, BUF_LEN)
2778 .expect("failed to read back buffer");
2779 assert_eq!(
2780 read_back,
2781 vec![0u8; BUF_LEN],
2782 "{name} should report nothing currently active"
2783 );
2784 }
2785 })
2786 .await;
2787 }
2788
2789 #[::fuchsia::test]
2790 async fn keyboard_input_file_ioctls() {
2791 spawn_kernel_and_run(async move |current_task| {
2792 let inspector = fuchsia_inspect::Inspector::default();
2793 let keyboard_device = InputDevice::new_keyboard(&inspector.root());
2794 let file = keyboard_device
2795 .open_test(¤t_task)
2796 .expect("Failed to open keyboard input file");
2797
2798 let user_addr = map_memory(¤t_task, UserAddress::default(), 4096);
2799
2800 let name_req = crate::input_file::EVIOCGNAME_BASE | (256 << 16);
2802 let res = file.ioctl(¤t_task, name_req, user_addr.into());
2803 assert!(res.is_ok());
2804
2805 let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
2807 let res = file.ioctl(¤t_task, prop_req, user_addr.into());
2808 assert_eq!(res, Ok(SUCCESS));
2809 let prop_buf = current_task.read_memory_to_vec(user_addr, 8).unwrap();
2810 let byte_idx = (uapi::INPUT_PROP_DIRECT / 8) as usize;
2811 let bit_idx = uapi::INPUT_PROP_DIRECT % 8;
2812 assert_ne!(prop_buf[byte_idx] & (1 << bit_idx), 0);
2813
2814 let key_req = crate::input_file::EVIOCGBIT_EV_KEY_BASE | (64 << 16);
2816 let res = file.ioctl(¤t_task, key_req, user_addr.into());
2817 assert_eq!(res, Ok(SUCCESS));
2818 let key_buf = current_task.read_memory_to_vec(user_addr, 64).unwrap();
2819
2820 let check_key = |key_code: u32| {
2821 let byte = (key_code / 8) as usize;
2822 let bit = key_code % 8;
2823 assert_ne!(
2824 key_buf[byte] & (1 << bit),
2825 0,
2826 "Keycode {} should be set in evdev bitmask",
2827 key_code
2828 );
2829 };
2830 check_key(uapi::KEY_A);
2831 check_key(uapi::KEY_Z);
2832 check_key(uapi::KEY_1);
2833 check_key(uapi::KEY_ENTER);
2834 check_key(uapi::KEY_SPACE);
2835 check_key(uapi::KEY_ESC);
2836 check_key(uapi::KEY_POWER);
2837 check_key(uapi::KEY_VOLUMEUP);
2838 check_key(uapi::KEY_VOLUMEDOWN);
2839
2840 let check_key_absent = |key_code: u32, name: &str| {
2845 let byte = (key_code / 8) as usize;
2846 let bit = key_code % 8;
2847 assert_eq!(
2848 key_buf[byte] & (1 << bit),
2849 0,
2850 "{} ({}) should NOT be set in the keyboard evdev bitmask",
2851 name,
2852 key_code
2853 );
2854 };
2855 check_key_absent(uapi::BTN_LEFT, "BTN_LEFT");
2856 check_key_absent(uapi::BTN_TRIGGER, "BTN_TRIGGER");
2857 check_key_absent(uapi::BTN_SOUTH, "BTN_SOUTH");
2858 check_key_absent(uapi::BTN_THUMBR, "BTN_THUMBR");
2859 })
2860 .await;
2861 }
2862
2863 #[::fuchsia::test]
2864 async fn keyboard_events_update_inspect_counters() {
2865 spawn_kernel_and_run(async move |current_task| {
2866 let inspector = fuchsia_inspect::Inspector::default();
2867 let (_keyboard_device, keyboard_file, keyboard_listener) =
2868 start_keyboard_input_inspect(¤t_task, &inspector).await;
2869
2870 let key_event = fuiinput::KeyEvent {
2871 timestamp: Some(12345),
2872 type_: Some(fuiinput::KeyEventType::Pressed),
2873 key: Some(fidl_fuchsia_input::Key::A),
2874 ..Default::default()
2875 };
2876
2877 let _ = keyboard_listener.on_key_event(&key_event).await;
2878 std::mem::drop(keyboard_listener);
2879
2880 let events = read_uapi_events(&keyboard_file, ¤t_task);
2882 assert_eq!(events.len(), 2);
2883
2884 assert_data_tree!(inspector, root: {
2888 keyboard_device: {
2889 active_wake_leases_count: AnyProperty,
2890 total_events_with_wake_lease_count: AnyProperty,
2891 total_fidl_events_received_count: 1u64,
2892 total_fidl_events_ignored_count: 0u64,
2893 total_fidl_events_unexpected_count: 0u64,
2894 total_fidl_events_converted_count: 1u64,
2895 total_uapi_events_generated_count: 2u64,
2896 last_generated_uapi_event_timestamp_ns: 12345i64,
2897 keyboard_file_0: {
2898 fidl_events_received_count: 1u64,
2899 fidl_events_ignored_count: 0u64,
2900 fidl_events_unexpected_count: 0u64,
2901 fidl_events_converted_count: 1u64,
2902 uapi_events_generated_count: 2u64,
2903 uapi_events_read_count: 2u64,
2904 fd_read_count: AnyProperty,
2905 fd_notify_count: AnyProperty,
2906 last_generated_uapi_event_timestamp_ns: 12345i64,
2907 last_read_uapi_event_timestamp_ns: AnyProperty,
2908 opened_without_nonblock: AnyProperty,
2909 open_timestamp_ns: AnyProperty,
2910 closed: AnyProperty,
2911 close_timestamp_ns: AnyProperty,
2912 }
2913 }
2914 });
2915 })
2916 .await;
2917 }
2918}