1use crate::presentation_loop;
6use async_utils::event::Event as AsyncEvent;
7use async_utils::hanging_get::client::HangingGetStream;
8use euclid::{Point2D, Transform2D};
9use fidl::endpoints::{ServerEnd, create_proxy, create_request_stream};
10use fidl_fuchsia_ui_input3::{self as ui_input3, KeyEvent};
11use fidl_fuchsia_ui_pointer::{
12 self as ui_pointer, MouseEvent, TouchEvent, TouchInteractionId, TouchInteractionStatus,
13 TouchResponse,
14};
15use futures::StreamExt;
16use futures::channel::{mpsc, oneshot};
17use log::info;
18use std::cell::{OnceCell, RefCell};
19use std::collections::HashMap;
20use std::rc::Rc;
21use std::slice::Iter;
22use {
23 fidl_fuchsia_math as fmath, fidl_fuchsia_ui_composition as ui_comp,
24 fidl_fuchsia_ui_test_conformance as ui_conformance, fidl_fuchsia_ui_test_input as test_input,
25 fidl_fuchsia_ui_views as ui_views, fuchsia_async as fasync, fuchsia_scenic as scenic,
26};
27
28pub type FlatlandPtr = Rc<ui_comp::FlatlandProxy>;
29
30async fn request_present(presentation_sender: &presentation_loop::PresentationSender) {
32 let (sender, receiver) = oneshot::channel::<()>();
33 presentation_sender.unbounded_send(sender).expect("failed to request present");
34 _ = receiver.await;
35}
36
37struct TouchInteraction {
44 pending_events: Vec<test_input::TouchInputListenerReportTouchInputRequest>,
46 status: Option<ui_pointer::TouchInteractionStatus>,
47}
48
49struct EmbeddedViewIds {
51 transform_id: ui_comp::TransformId,
53
54 content_id: ui_comp::ContentId,
56}
57
58pub(super) struct View {
60 flatland: FlatlandPtr,
62
63 view_event_listener: OnceCell<fasync::Task<()>>,
65
66 presentation_sender: presentation_loop::PresentationSender,
68
69 id_generator: scenic::flatland::IdGenerator,
71
72 root_transform_id: ui_comp::TransformId,
74
75 logical_size: fmath::SizeU,
77
78 device_pixel_ratio: f32,
80
81 connected_to_display: bool,
83
84 touch_watcher_task: OnceCell<fasync::Task<()>>,
86
87 view_parameters: Option<ui_pointer::ViewParameters>,
90
91 touch_interactions: HashMap<TouchInteractionId, TouchInteraction>,
93
94 touch_input_listener: Option<test_input::TouchInputListenerProxy>,
96
97 mouse_watched_task: OnceCell<fasync::Task<()>>,
99
100 mouse_input_listener: Option<test_input::MouseInputListenerProxy>,
102
103 keyboard_watched_task: OnceCell<fasync::Task<()>>,
105
106 keyboard_input_listener: Option<test_input::KeyboardInputListenerProxy>,
108
109 embedded_views: HashMap<u64, EmbeddedViewIds>,
111}
112
113impl View {
114 pub async fn new(
115 flatland: ui_comp::FlatlandProxy,
116 keyboard_client: ui_input3::KeyboardProxy,
117 view_creation_token: ui_views::ViewCreationToken,
118 touch_input_listener: Option<test_input::TouchInputListenerProxy>,
119 mouse_input_listener: Option<test_input::MouseInputListenerProxy>,
120 keyboard_input_listener: Option<test_input::KeyboardInputListenerProxy>,
121 device_pixel_ratio: f32,
122 view_focuser: Option<ServerEnd<ui_views::FocuserMarker>>,
123 ) -> (Rc<RefCell<Self>>, ui_views::ViewRef) {
124 let flatland = Rc::new(flatland);
125 let (presentation_sender, presentation_receiver) = mpsc::unbounded();
126 presentation_loop::start_flatland_presentation_loop(
127 presentation_receiver,
128 Rc::downgrade(&flatland),
129 );
130
131 let mut id_generator = scenic::flatland::IdGenerator::new();
132
133 let (parent_viewport_watcher, parent_viewport_watcher_request) =
135 create_proxy::<ui_comp::ParentViewportWatcherMarker>();
136 let (touch_source, touch_source_request) = create_proxy::<ui_pointer::TouchSourceMarker>();
137 let (mouse_source, mouse_source_request) = create_proxy::<ui_pointer::MouseSourceMarker>();
138 let view_bound_protocols = ui_comp::ViewBoundProtocols {
139 touch_source: Some(touch_source_request),
140 mouse_source: Some(mouse_source_request),
141 view_focuser,
142 ..Default::default()
143 };
144 let view_ref_pair = scenic::ViewRefPair::new().expect("failed to create view ref pair");
145 let view_ref = scenic::duplicate_view_ref(&view_ref_pair.view_ref)
146 .expect("failed to duplicate view ref");
147 let view_ref_clone =
148 scenic::duplicate_view_ref(&view_ref).expect("failed to duplicate view ref");
149 let view_identity = ui_views::ViewIdentityOnCreation::from(view_ref_pair);
150
151 let root_transform_id = Self::create_transform(flatland.clone(), &mut id_generator);
153
154 flatland
156 .create_view2(
157 view_creation_token,
158 view_identity,
159 view_bound_protocols,
160 parent_viewport_watcher_request,
161 )
162 .expect("failed to create view");
163 flatland.set_root_transform(&root_transform_id).expect("failed to set root transform");
164 info!("[b/328261162] Wait for request_present()");
165 request_present(&presentation_sender).await;
166
167 let this = Rc::new(RefCell::new(Self {
168 flatland,
169 view_event_listener: OnceCell::new(),
170 presentation_sender,
171 id_generator,
172 root_transform_id,
173 logical_size: fmath::SizeU { width: 0, height: 0 },
174 device_pixel_ratio,
175 connected_to_display: false,
176 touch_watcher_task: OnceCell::new(),
177 view_parameters: None,
178 touch_interactions: HashMap::new(),
179 touch_input_listener,
180 mouse_watched_task: OnceCell::new(),
181 mouse_input_listener,
182 keyboard_watched_task: OnceCell::new(),
183 keyboard_input_listener,
184 embedded_views: HashMap::new(),
185 }));
186
187 let view_initialized = AsyncEvent::new();
188 let view_events_task = fasync::Task::local(Self::listen_for_view_events(
189 this.clone(),
190 parent_viewport_watcher,
191 view_initialized.clone(),
192 ));
193 this.borrow_mut()
194 .view_event_listener
195 .set(view_events_task)
196 .expect("set event listener task more than once");
197
198 let touch_task =
200 fasync::Task::local(Self::listen_for_touch_events(this.clone(), touch_source));
201 this.borrow_mut()
202 .touch_watcher_task
203 .set(touch_task)
204 .expect("set touch watcher task more than once");
205
206 let mouse_task =
207 fasync::Task::local(Self::listen_for_mouse_events(this.clone(), mouse_source));
208 this.borrow_mut()
209 .mouse_watched_task
210 .set(mouse_task)
211 .expect("set mouse watcher task more than once");
212
213 let keyboard_ready = AsyncEvent::new();
214 let keyboard_task = fasync::Task::local(Self::listen_for_key_events(
215 this.clone(),
216 keyboard_client,
217 view_ref,
218 keyboard_ready.clone(),
219 ));
220 this.borrow_mut()
221 .keyboard_watched_task
222 .set(keyboard_task)
223 .expect("set keyboard watcher task more than once");
224
225 info!("[b/328261162] Wait for view to be initialized.");
226 _ = view_initialized.wait().await;
227
228 info!("[b/328261162] Wait for keyboard listener ready.");
229 _ = keyboard_ready.wait().await;
230
231 (this, view_ref_clone)
232 }
233
234 fn is_initialized(&self) -> bool {
237 info!(
238 "connected to display = {} logical size = ({}, {})",
239 self.connected_to_display, self.logical_size.width, self.logical_size.height
240 );
241 self.connected_to_display && self.logical_size.width > 0 && self.logical_size.height > 0
242 }
243
244 async fn listen_for_view_events(
247 this: Rc<RefCell<Self>>,
248 parent_viewport_watcher: ui_comp::ParentViewportWatcherProxy,
249 view_initialized: AsyncEvent,
250 ) {
251 let mut view_initialized = Some(view_initialized);
252
253 let mut layout_info_stream = HangingGetStream::new(
254 parent_viewport_watcher.clone(),
255 ui_comp::ParentViewportWatcherProxy::get_layout,
256 );
257 let mut status_stream = HangingGetStream::new(
258 parent_viewport_watcher,
259 ui_comp::ParentViewportWatcherProxy::get_status,
260 );
261
262 loop {
263 futures::select! {
264 parent_status = status_stream.select_next_some() => {
265 match parent_status {
266 Ok(status) => {
267 info!("received parent status update");
268 this.borrow_mut().update_parent_status(status);
269 }
270 Err(fidl::Error::ClientChannelClosed{..}) => {
273 break;
274 }
275 Err(e) => {
276 panic!("get_status got unexpected error {:?}", e);
277 }
278 }
279 }
280 layout_info = layout_info_stream.select_next_some() => {
281 match layout_info {
282 Ok(layout_info) => {
283 this.borrow_mut().update_view_parameters(layout_info.logical_size, layout_info.device_pixel_ratio);
284 }
285 Err(fidl::Error::ClientChannelClosed{..}) => {
288 break;
289 }
290 Err(e) => {
291 panic!("get_layout got unexpected error {:?}", e);
292 }
293 }
294 }
295 }
296
297 if view_initialized.is_some() && this.borrow().is_initialized() {
299 view_initialized.take().expect("failed to take view initialized sender").signal();
300 }
301 }
302 }
303
304 pub async fn embed_remote_view(
306 &mut self,
307 id: u64,
308 properties: ui_conformance::EmbeddedViewProperties,
309 ) -> ui_views::ViewCreationToken {
310 let view_bounds = properties.bounds.expect("missing embedded view bounds");
311
312 let transform_id = Self::create_transform(self.flatland.clone(), &mut self.id_generator);
314
315 let content_id = self.id_generator.next_content_id();
317
318 let token_pair = scenic::flatland::ViewCreationTokenPair::new()
320 .expect("failed to create view creation token pair");
321
322 let (_, child_view_watcher_request) = create_proxy::<ui_comp::ChildViewWatcherMarker>();
324 self.flatland
325 .create_viewport(
326 &content_id,
327 token_pair.viewport_creation_token,
328 &ui_comp::ViewportProperties {
329 logical_size: view_bounds.size,
330 ..Default::default()
331 },
332 child_view_watcher_request,
333 )
334 .expect("failed to create child viewport");
335
336 self.flatland
338 .set_content(&transform_id, &content_id)
339 .expect("failed to set viewport content");
340
341 if let Some(origin) = view_bounds.origin {
343 self.flatland
344 .set_translation(&transform_id, &origin)
345 .expect("failed to position embedded view");
346 }
347
348 self.flatland
350 .add_child(&self.root_transform_id, &transform_id)
351 .expect("failed to attach embedded view to root transform");
352
353 request_present(&self.presentation_sender).await;
355
356 self.embedded_views.insert(id, EmbeddedViewIds { transform_id, content_id });
357
358 token_pair.view_creation_token
359 }
360
361 pub async fn set_embedded_view_properties(
362 &mut self,
363 id: u64,
364 properties: ui_conformance::EmbeddedViewProperties,
365 ) {
366 let view_bounds = properties.bounds.expect("missing embedded view bounds");
367
368 let embedded_view =
370 self.embedded_views.get_mut(&id).expect("no embedded view with specified id");
371
372 self.flatland
374 .set_viewport_properties(
375 &embedded_view.content_id,
376 &ui_comp::ViewportProperties {
377 logical_size: view_bounds.size,
378 ..Default::default()
379 },
380 )
381 .expect("failed to set viewport properties");
382 if let Some(origin) = view_bounds.origin {
383 self.flatland
384 .set_translation(&embedded_view.transform_id, &origin)
385 .expect("failed to position embedded view");
386 }
387
388 request_present(&self.presentation_sender).await;
390 }
391
392 fn create_transform(
394 flatland: FlatlandPtr,
395 id_generator: &mut scenic::flatland::IdGenerator,
396 ) -> ui_comp::TransformId {
397 let flatland_transform_id = id_generator.next_transform_id();
398
399 flatland.create_transform(&flatland_transform_id).expect("failed to create transform");
400
401 flatland_transform_id
402 }
403
404 fn update_view_parameters(
406 &mut self,
407 logical_size: Option<fmath::SizeU>,
408 device_pixel_ratio: Option<fmath::VecF>,
409 ) {
410 if let Some(size) = logical_size {
411 self.logical_size = size;
412 }
413
414 if let Some(dpr) = device_pixel_ratio {
415 assert!(dpr.x == dpr.y);
416 self.device_pixel_ratio = dpr.x;
417 }
418 }
419
420 fn update_parent_status(&mut self, parent_status: ui_comp::ParentViewportStatus) {
422 self.connected_to_display = match parent_status {
423 ui_comp::ParentViewportStatus::ConnectedToDisplay => true,
424 ui_comp::ParentViewportStatus::DisconnectedFromDisplay => false,
425 };
426 }
427
428 fn ensure_interaction_exists(&mut self, id: &TouchInteractionId) {
430 if !self.touch_interactions.contains_key(id) {
431 self.touch_interactions
432 .insert(id.clone(), TouchInteraction { pending_events: vec![], status: None });
433 }
434 }
435
436 fn get_touch_report(
437 &self,
438 touch_event: &ui_pointer::TouchEvent,
439 ) -> test_input::TouchInputListenerReportTouchInputRequest {
440 let pointer_sample =
441 touch_event.pointer_sample.as_ref().expect("touch event missing pointer_sample");
442 let position_in_viewport = pointer_sample
443 .position_in_viewport
444 .expect("pointer sample missing position_in_viewport");
445 let local_position =
446 self.get_local_position(Point2D::new(position_in_viewport[0], position_in_viewport[1]));
447 let interact = pointer_sample.interaction.expect("interaction is missing");
448 let pointer_id = interact.pointer_id;
449 let device_id = interact.device_id;
450
451 let local_x: f64 = local_position.x.try_into().expect("failed to convert to f64");
452 let local_y: f64 = local_position.y.try_into().expect("failed to convert to f64");
453 let view_bounds = self.view_parameters.expect("missing view parameters").view;
454 let view_min_x: f64 = view_bounds.min[0].try_into().expect("failed to convert to f64");
455 let view_min_y: f64 = view_bounds.min[1].try_into().expect("failed to convert to f64");
456 let view_max_x: f64 = view_bounds.max[0].try_into().expect("failed to convert to f64");
457 let view_max_y: f64 = view_bounds.max[1].try_into().expect("failed to convert to f64");
458
459 info!("view min ({:?}, {:?})", view_min_x, view_min_y);
460 info!("view max ({:?}, {:?})", view_max_x, view_max_y);
461 info!("tap received at ({:?}, {:?})", local_x, local_y);
462
463 test_input::TouchInputListenerReportTouchInputRequest {
464 local_x: Some(local_x),
465 local_y: Some(local_y),
466 phase: pointer_sample.phase,
467 pointer_id: Some(pointer_id),
468 time_received: touch_event.timestamp,
469 device_pixel_ratio: Some(self.device_pixel_ratio as f64),
470 device_id: Some(device_id),
471 ..Default::default()
472 }
473 }
474
475 fn get_local_position(&self, position_in_viewpoint: Point2D<f32, f32>) -> Point2D<f32, f32> {
476 let viewport_to_view_transform =
477 self.view_parameters.expect("missing view parameters").viewport_to_view_transform;
478 Transform2D::new(
479 viewport_to_view_transform[0],
480 viewport_to_view_transform[3],
481 viewport_to_view_transform[1],
482 viewport_to_view_transform[4],
483 viewport_to_view_transform[6],
484 viewport_to_view_transform[7],
485 )
486 .transform_point(position_in_viewpoint)
487 }
488
489 fn process_touch_events(
490 &mut self,
491 events: Vec<ui_pointer::TouchEvent>,
492 ) -> Vec<ui_pointer::TouchResponse> {
493 let pending_responses = Self::generate_touch_event_responses(events.iter());
496
497 for e in events.iter() {
498 if let Some(view_parameters) = e.view_parameters {
499 self.view_parameters = Some(view_parameters);
500 }
501
502 if let Some(ui_pointer::TouchPointerSample { interaction: Some(id), .. }) =
504 &e.pointer_sample
505 {
506 self.ensure_interaction_exists(&id);
507 let interaction_status =
508 self.touch_interactions.get(id).expect("interaction does not exist").status;
509 match interaction_status {
510 None => {
511 let touch_report = self.get_touch_report(&e);
513 self.touch_interactions
514 .get_mut(&id)
515 .unwrap()
516 .pending_events
517 .push(touch_report);
518 }
519 Some(TouchInteractionStatus::Granted) => {
520 match &self.touch_input_listener {
521 Some(listener) => {
522 let touch_report = self.get_touch_report(&e);
525 listener
526 .report_touch_input(&touch_report)
527 .expect("failed to send touch input report");
528 }
529 None => {
530 info!("no touch event listener.");
531 }
532 }
533 }
534 Some(TouchInteractionStatus::Denied) => {
535 self.touch_interactions.remove(&id);
539 }
540 }
541 }
542
543 if let Some(ui_pointer::TouchInteractionResult { interaction: id, status }) =
545 &e.interaction_result
546 {
547 self.ensure_interaction_exists(&id);
548 let interaction = self.touch_interactions.get_mut(&id).unwrap();
549 if let Some(existing_status) = &interaction.status {
550 assert_eq!(status, existing_status);
552 } else {
553 interaction.status = Some(status.clone());
555 }
556
557 match status {
558 ui_pointer::TouchInteractionStatus::Granted => {
559 let mut pending_events = vec![];
561 std::mem::swap(
562 &mut pending_events,
563 &mut self.touch_interactions.get_mut(&id).unwrap().pending_events,
564 );
565 for pending_event in pending_events {
566 match &self.touch_input_listener {
567 Some(listener) => {
568 listener
569 .report_touch_input(&pending_event)
570 .expect("failed to send touch input report");
571 }
572 None => {
573 info!("no touch event listener.");
574 }
575 }
576 }
577 }
578 ui_pointer::TouchInteractionStatus::Denied => {
579 self.touch_interactions.remove(&id);
583 }
584 }
585 }
586 }
587
588 pending_responses
589 }
590
591 fn generate_touch_event_responses(events: Iter<'_, TouchEvent>) -> Vec<TouchResponse> {
594 events
595 .map(|evt| {
596 if let Some(_) = &evt.pointer_sample {
597 return TouchResponse {
598 response_type: Some(ui_pointer::TouchResponseType::Yes),
599 trace_flow_id: evt.trace_flow_id,
600 ..Default::default()
601 };
602 }
603 TouchResponse::default()
604 })
605 .collect()
606 }
607
608 async fn listen_for_touch_events(
609 this: Rc<RefCell<Self>>,
610 touch_source: ui_pointer::TouchSourceProxy,
611 ) {
612 let mut pending_responses: Vec<TouchResponse> = vec![];
613
614 loop {
615 let events = touch_source.watch(&pending_responses);
616
617 match events.await {
618 Ok(events) => {
619 pending_responses = this.borrow_mut().process_touch_events(events);
620 }
621 _ => {
622 info!("TouchSource connection closed");
623 return;
624 }
625 }
626 }
627 }
628
629 fn get_mouse_report(
630 &self,
631 mouse_event: &ui_pointer::MouseEvent,
632 ) -> test_input::MouseInputListenerReportMouseInputRequest {
633 let pointer_sample =
634 mouse_event.pointer_sample.as_ref().expect("mouse event missing pointer_sample");
635 let position_in_viewport = pointer_sample
636 .position_in_viewport
637 .expect("pointer sample missing position_in_viewport");
638 let local_position =
639 self.get_local_position(Point2D::new(position_in_viewport[0], position_in_viewport[1]));
640 let local_x: f64 = local_position.x.try_into().expect("failed to convert to f64");
641 let local_y: f64 = local_position.y.try_into().expect("failed to convert to f64");
642 let device_id = pointer_sample.device_id.expect("pointer sample missing device id");
643
644 let buttons: Option<Vec<test_input::MouseButton>> = match &pointer_sample.pressed_buttons {
645 None => None,
646 Some(buttons) => Some(
647 buttons
648 .into_iter()
649 .map(|button| {
650 test_input::MouseButton::from_primitive_allow_unknown(*button as u32)
651 })
652 .collect(),
653 ),
654 };
655
656 test_input::MouseInputListenerReportMouseInputRequest {
657 local_x: Some(local_x),
658 local_y: Some(local_y),
659 time_received: mouse_event.timestamp,
660 buttons,
661 device_pixel_ratio: Some(self.device_pixel_ratio as f64),
662 wheel_x_physical_pixel: pointer_sample.scroll_h_physical_pixel,
663 wheel_y_physical_pixel: pointer_sample.scroll_v_physical_pixel,
664 device_id: Some(device_id),
665 ..Default::default()
666 }
667 }
668
669 fn process_mouse_events(&mut self, events: Vec<MouseEvent>) {
670 for mouse_event in events {
671 match &mouse_event {
672 &MouseEvent {
673 stream_info:
674 Some(ui_pointer::MouseEventStreamInfo {
675 status: ui_pointer::MouseViewStatus::Exited,
676 ..
677 }),
678 ..
679 } => return,
680 &MouseEvent {
681 stream_info:
682 Some(ui_pointer::MouseEventStreamInfo {
683 status: ui_pointer::MouseViewStatus::Entered,
684 ..
685 }),
686 ..
687 } => {
688 let view_parameters =
689 mouse_event.view_parameters.expect("entered event missing view_parameters");
690 self.view_parameters = Some(view_parameters);
691 continue;
692 }
693 _ => {}
694 }
695 let event = self.get_mouse_report(&mouse_event);
696 match &self.mouse_input_listener {
697 Some(listener) => {
698 listener.report_mouse_input(&event).expect("failed to send mouse input report");
699 }
700 None => {
701 info!("no mouse event listener");
702 }
703 }
704 }
705 }
706
707 async fn listen_for_mouse_events(
708 this: Rc<RefCell<Self>>,
709 mouse_source: ui_pointer::MouseSourceProxy,
710 ) {
711 loop {
712 let events = mouse_source.watch();
713 match events.await {
714 Ok(events) => {
715 this.borrow_mut().process_mouse_events(events);
716 }
717 _ => {
718 info!("MouseSource connection closed");
719 return;
720 }
721 }
722 }
723 }
724
725 fn get_key_report(
726 &self,
727 key_event: KeyEvent,
728 ) -> Option<test_input::KeyboardInputListenerReportTextInputRequest> {
729 if key_event.type_ != Some(ui_input3::KeyEventType::Pressed) {
730 return None;
731 }
732 match key_event.key_meaning.unwrap() {
733 ui_input3::KeyMeaning::Codepoint(code) => {
734 let s = char::from_u32(code).expect("key event is not a valid char").to_string();
735 info!("Key received {:?}", s);
736 Some(test_input::KeyboardInputListenerReportTextInputRequest {
737 text: Some(s),
738 device_id: key_event.device_id,
739 ..Default::default()
740 })
741 }
742 ui_input3::KeyMeaning::NonPrintableKey(key) => {
743 info!("NonPrintableKey received {:?}", key);
744 Some(test_input::KeyboardInputListenerReportTextInputRequest {
745 non_printable: Some(key),
746 device_id: key_event.device_id,
747 ..Default::default()
748 })
749 }
750 }
751 }
752
753 fn process_key_event(&mut self, event: KeyEvent) {
754 let report = self.get_key_report(event);
755 match &self.keyboard_input_listener {
756 Some(listener) => match report {
757 Some(event) => {
758 listener
759 .report_text_input(&event)
760 .expect("failed to send keyboard input report");
761 }
762 None => {}
763 },
764 None => {
765 info!("no keyboard event listener");
766 }
767 }
768 }
769
770 async fn listen_for_key_events(
771 this: Rc<RefCell<Self>>,
772 keyboard: ui_input3::KeyboardProxy,
773 view_ref: ui_views::ViewRef,
774 keyboard_ready: AsyncEvent,
775 ) {
776 let (keyboard_client, mut keyboard_stream) =
777 create_request_stream::<ui_input3::KeyboardListenerMarker>();
778
779 keyboard
780 .add_listener(view_ref, keyboard_client)
781 .await
782 .expect("failed to add keyboard listener");
783
784 keyboard_ready.signal();
785
786 loop {
787 let listener_request = keyboard_stream.next().await;
788 match listener_request {
789 Some(Ok(ui_input3::KeyboardListenerRequest::OnKeyEvent {
790 event,
791 responder,
792 ..
793 })) => {
794 responder.send(ui_input3::KeyEventStatus::Handled).expect("send");
795 this.borrow_mut().process_key_event(event);
796 }
797 _ => {
798 info!("keyboard connection closed");
799 return;
800 }
801 }
802 }
803 }
804}