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