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