1use crate::app::strategies::framebuffer::{CoordinatorProxyPtr, DisplayId};
6use crate::app::{Config, MessageInternal};
7use crate::drawing::DisplayRotation;
8use crate::render::generic::{self, Backend};
9use crate::render::{Context as RenderContext, ContextInner};
10use crate::view::strategies::base::{ViewStrategy, ViewStrategyPtr};
11use crate::view::{
12 DisplayInfo, UserInputMessage, ViewAssistantContext, ViewAssistantPtr, ViewDetails,
13};
14use crate::{IntPoint, IntSize, Size, ViewKey, input};
15use anyhow::{Context, Error, bail, ensure};
16use async_trait::async_trait;
17use display_utils::{
18 BufferCollectionId, EventId, INVALID_LAYER_ID, ImageId as DisplayImageId, LayerId, PixelFormat,
19};
20use euclid::size2;
21use fidl_fuchsia_hardware_display::{
22 ConfigStamp, CoordinatorCommitConfigRequest, CoordinatorListenerRequest, CoordinatorProxy,
23 INVALID_CONFIG_STAMP_VALUE,
24};
25use fidl_fuchsia_hardware_display_types::{INVALID_DISP_ID, ImageBufferUsage, ImageMetadata};
26use fuchsia_async::{self as fasync};
27use fuchsia_framebuffer::sysmem::BufferCollectionAllocator;
28use fuchsia_framebuffer::{FrameSet, FrameUsage, ImageId};
29use fuchsia_trace::{duration, instant};
30use futures::channel::mpsc::UnboundedSender;
31use std::collections::{BTreeMap, BTreeSet, VecDeque};
32use std::sync::atomic::{AtomicU64, Ordering};
33use zx::{Event, HandleBased, MonotonicDuration, MonotonicInstant, Status};
34
35type WaitEvents = BTreeMap<ImageId, (Event, EventId)>;
36
37struct BusyImage {
38 stamp: ConfigStamp,
39 view_key: ViewKey,
40 image_id: DisplayImageId,
41 collection_id: BufferCollectionId,
42}
43
44#[derive(Default)]
45struct CollectionIdGenerator {}
46
47impl Iterator for CollectionIdGenerator {
48 type Item = BufferCollectionId;
49
50 fn next(&mut self) -> Option<BufferCollectionId> {
51 static NEXT_ID_VALUE: AtomicU64 = AtomicU64::new(100);
52 let value = NEXT_ID_VALUE.fetch_add(1, Ordering::Relaxed);
55 if value == 0 { None } else { Some(BufferCollectionId(value)) }
58 }
59}
60fn next_collection_id() -> BufferCollectionId {
61 CollectionIdGenerator::default().next().expect("collection_id")
62}
63
64#[derive(Default)]
65struct ImageIdGenerator {}
66
67impl Iterator for ImageIdGenerator {
68 type Item = u64;
69
70 fn next(&mut self) -> Option<u64> {
71 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
72 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
75 if id == 0 { None } else { Some(id) }
78 }
79}
80fn next_image_id() -> u64 {
81 ImageIdGenerator::default().next().expect("image_id")
82}
83
84#[derive(Default)]
85struct LayerIdGenerator {}
86
87impl Iterator for LayerIdGenerator {
88 type Item = LayerId;
89
90 fn next(&mut self) -> Option<LayerId> {
91 static NEXT_ID_VALUE: AtomicU64 = AtomicU64::new(1);
92 let value = NEXT_ID_VALUE.fetch_add(1, Ordering::Relaxed);
95 if value == 0 { None } else { Some(LayerId(value)) }
98 }
99}
100fn next_layer_id() -> LayerId {
101 LayerIdGenerator::default().next().expect("layer_id")
102}
103
104async fn create_and_import_event(
105 coordinator: &CoordinatorProxy,
106) -> Result<(Event, EventId), Error> {
107 let event = Event::create();
108
109 let their_event = event.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
110 let event_id_value = event.koid()?.raw_koid();
111 let event_id = EventId(event_id_value);
112 coordinator.import_event(Event::from_handle(their_event.into_handle()), &event_id.into())?;
113 Ok((event, event_id))
114}
115
116fn size_from_info(info: &fidl_fuchsia_hardware_display::Info, mode_idx: usize) -> IntSize {
117 let mode = &info.modes[mode_idx];
118 size2(mode.active_area.width, mode.active_area.height).to_i32()
119}
120
121#[derive(Debug, Clone)]
122pub struct Display {
123 pub coordinator: CoordinatorProxyPtr,
124 pub display_id: DisplayId,
125 pub info: fidl_fuchsia_hardware_display::Info,
126 pub layer_id: LayerId,
127 pub mode_idx: usize,
128}
129
130impl Display {
131 pub async fn new(
132 coordinator: CoordinatorProxyPtr,
133 display_id: DisplayId,
134 info: fidl_fuchsia_hardware_display::Info,
135 ) -> Result<Self, Error> {
136 Ok(Self { coordinator, display_id, info, layer_id: INVALID_LAYER_ID, mode_idx: 0 })
137 }
138
139 pub fn set_mode(&mut self, mode_idx: usize) -> Result<(), Error> {
140 self.coordinator.set_display_mode(&self.info.id, &self.info.modes[mode_idx])?;
141 self.mode_idx = mode_idx;
142 Ok(())
143 }
144
145 pub async fn create_layer(&mut self) -> Result<(), Error> {
146 let layer_id = next_layer_id();
147 let result = self.coordinator.create_layer(&layer_id.into()).await?;
148 match result {
149 Ok(()) => {
150 self.layer_id = layer_id.into();
151 Ok(())
152 }
153 Err(status) => {
154 bail!("Display::new(): failed to create layer {}", Status::from_raw(status))
155 }
156 }
157 }
158
159 pub fn size(&self) -> IntSize {
160 size_from_info(&self.info, self.mode_idx)
161 }
162
163 pub fn pixel_format(&self) -> PixelFormat {
164 self.info.pixel_format[0].into()
165 }
166}
167
168struct DisplayResources {
169 pub frame_set: FrameSet,
170 pub image_indexes: BTreeMap<ImageId, u32>,
171 pub context: RenderContext,
172 pub wait_events: WaitEvents,
173 pub busy_images: VecDeque<BusyImage>,
174}
175
176const RENDER_FRAME_COUNT: usize = 2;
177
178pub(crate) struct DisplayDirectViewStrategy {
179 key: ViewKey,
180 display: Display,
181 app_sender: UnboundedSender<MessageInternal>,
182 display_rotation: DisplayRotation,
183 display_resources: Option<DisplayResources>,
184 drop_display_resources_task: Option<fasync::Task<()>>,
185 display_resource_release_delay: std::time::Duration,
186 vsync_phase: MonotonicInstant,
187 vsync_interval: MonotonicDuration,
188 mouse_cursor_position: Option<IntPoint>,
189 pub collection_id: BufferCollectionId,
190 render_frame_count: usize,
191 last_config_stamp: u64,
192 presented: Option<u64>,
193}
194
195impl DisplayDirectViewStrategy {
196 pub async fn new(
197 key: ViewKey,
198 coordinator: CoordinatorProxyPtr,
199 app_sender: UnboundedSender<MessageInternal>,
200 info: fidl_fuchsia_hardware_display::Info,
201 preferred_size: IntSize,
202 ) -> Result<ViewStrategyPtr, Error> {
203 let app_config = Config::get();
204 let collection_id = next_collection_id();
205 let render_frame_count = app_config.buffer_count.unwrap_or(RENDER_FRAME_COUNT);
206
207 let mode_idx = info
209 .modes
210 .iter()
211 .position(|mode| {
212 let size = size2(mode.active_area.width, mode.active_area.height).to_i32();
213 size == preferred_size
214 })
215 .unwrap_or(0);
216
217 let mut display = Display::new(coordinator, info.id.into(), info).await?;
218
219 if mode_idx != 0 {
220 display.set_mode(mode_idx)?;
221 }
222 display.create_layer().await?;
223
224 let display_resources = Self::allocate_display_resources(
225 collection_id,
226 display.size(),
227 display.pixel_format(),
228 render_frame_count,
229 &display,
230 )
231 .await?;
232
233 app_sender.unbounded_send(MessageInternal::Render(key)).expect("unbounded_send");
234 app_sender.unbounded_send(MessageInternal::Focus(key, true)).expect("unbounded_send");
235
236 Ok(Box::new(Self {
237 key,
238 display,
239 app_sender,
240 display_rotation: app_config.display_rotation,
241 display_resources: Some(display_resources),
242 drop_display_resources_task: None,
243 display_resource_release_delay: app_config.display_resource_release_delay,
244 vsync_phase: MonotonicInstant::get(),
245 vsync_interval: MonotonicDuration::from_millis(16),
246 mouse_cursor_position: None,
247 collection_id,
248 render_frame_count,
249 last_config_stamp: INVALID_CONFIG_STAMP_VALUE,
250 presented: None,
251 }))
252 }
253
254 fn make_context(
255 &mut self,
256 view_details: &ViewDetails,
257 image_id: Option<ImageId>,
258 ) -> ViewAssistantContext {
259 let time_now = MonotonicInstant::get();
260 let mut interval_offset = MonotonicDuration::from_nanos(
263 (self.vsync_phase.into_nanos() - time_now.into_nanos())
264 % self.vsync_interval.into_nanos(),
265 );
266 if interval_offset != MonotonicDuration::from_nanos(0) && self.vsync_phase < time_now {
269 interval_offset += self.vsync_interval;
270 }
271
272 let display_rotation = self.display_rotation;
273 let app_sender = self.app_sender.clone();
274 let mouse_cursor_position = self.mouse_cursor_position.clone();
275 let (image_index, actual_image_id) = image_id
276 .and_then(|available| {
277 Some((
278 *self.display_resources().image_indexes.get(&available).expect("image_index"),
279 available,
280 ))
281 })
282 .unwrap_or_default();
283
284 ViewAssistantContext {
285 key: view_details.key,
286 size: match display_rotation {
287 DisplayRotation::Deg0 | DisplayRotation::Deg180 => view_details.physical_size,
288 DisplayRotation::Deg90 | DisplayRotation::Deg270 => {
289 size2(view_details.physical_size.height, view_details.physical_size.width)
290 }
291 },
292 metrics: view_details.metrics,
293 presentation_time: time_now + interval_offset,
294 buffer_count: None,
295 image_id: actual_image_id,
296 image_index: image_index,
297 app_sender,
298 mouse_cursor_position,
299 display_info: Some(DisplayInfo::from(&self.display.info)),
300 }
301 }
302
303 async fn allocate_display_resources(
304 collection_id: BufferCollectionId,
305 size: IntSize,
306 pixel_format: display_utils::PixelFormat,
307 render_frame_count: usize,
308 display: &Display,
309 ) -> Result<DisplayResources, Error> {
310 let app_config = Config::get();
311 let use_spinel = app_config.use_spinel;
312
313 ensure!(use_spinel == false, "Spinel support is disabled");
314
315 let display_rotation = app_config.display_rotation;
316 let unsize = size.floor().to_u32();
317
318 let usage = if use_spinel { FrameUsage::Gpu } else { FrameUsage::Cpu };
319 let mut buffer_allocator = BufferCollectionAllocator::new(
320 unsize.width,
321 unsize.height,
322 pixel_format.into(),
323 usage,
324 render_frame_count,
325 )?;
326
327 buffer_allocator.set_name(100, "CarnelianDirect")?;
328
329 let context_token = buffer_allocator.duplicate_token().await?;
330 let context = RenderContext {
331 inner: ContextInner::Forma(generic::Forma::new_context(
332 context_token,
333 unsize,
334 display_rotation,
335 )),
336 };
337
338 let coordinator_token = buffer_allocator.duplicate_token().await?;
339 display
342 .coordinator
343 .import_buffer_collection(&collection_id.into(), coordinator_token)
344 .await?
345 .map_err(zx::Status::from_raw)?;
346 display
347 .coordinator
348 .set_buffer_collection_constraints(
349 &collection_id.into(),
350 &ImageBufferUsage {
351 tiling_type: fidl_fuchsia_hardware_display_types::IMAGE_TILING_TYPE_LINEAR,
352 },
353 )
354 .await?
355 .map_err(zx::Status::from_raw)?;
356
357 let buffers = buffer_allocator
358 .allocate_buffers(true)
359 .await
360 .context(format!("view: {:?} allocate_buffers", display.display_id))?;
361
362 ensure!(
363 buffers.settings.as_ref().unwrap().image_format_constraints.is_some(),
364 "No image format constraints"
365 );
366 ensure!(
367 buffers
368 .settings
369 .as_ref()
370 .unwrap()
371 .image_format_constraints
372 .as_ref()
373 .unwrap()
374 .pixel_format_modifier
375 .is_some(),
376 "Sysmem will always set pixel_format_modifier"
377 );
378 ensure!(
379 buffers.buffers.as_ref().unwrap().len() == render_frame_count,
380 "Buffers do not match frame count"
381 );
382
383 let image_tiling_type = match buffers
384 .settings
385 .as_ref()
386 .unwrap()
387 .image_format_constraints
388 .as_ref()
389 .unwrap()
390 .pixel_format_modifier
391 .as_ref()
392 .unwrap()
393 {
394 fidl_fuchsia_images2::PixelFormatModifier::IntelI915XTiled => 1,
395 fidl_fuchsia_images2::PixelFormatModifier::IntelI915YTiled => 2,
396 _ => fidl_fuchsia_hardware_display_types::IMAGE_TILING_TYPE_LINEAR,
397 };
398
399 let image_metadata = ImageMetadata {
400 dimensions: fidl_fuchsia_math::SizeU { width: unsize.width, height: unsize.height },
401 tiling_type: image_tiling_type,
402 };
403
404 let mut image_ids = BTreeSet::new();
405 let mut image_indexes = BTreeMap::new();
406 let mut wait_events = WaitEvents::new();
407 let buffer_count = buffers.buffers.as_ref().unwrap().len();
408 for index in 0..buffer_count as usize {
409 let uindex = index as u32;
410 let image_id = next_image_id();
411 let display_image_id = DisplayImageId(image_id);
412 display
413 .coordinator
414 .import_image(
415 &image_metadata,
416 &collection_id.into(),
417 uindex,
418 &display_image_id.into(),
419 )
420 .await
421 .context("FIDL coordinator import_image")?
422 .map_err(zx::Status::from_raw)
423 .context("import image error")?;
424
425 image_ids.insert(image_id as u64);
426 image_indexes.insert(image_id as u64, uindex);
427
428 let (event, event_id) = create_and_import_event(&display.coordinator).await?;
429 wait_events.insert(image_id as ImageId, (event, event_id));
430 }
431
432 let frame_set = FrameSet::new(collection_id, image_ids);
433
434 display.coordinator.set_layer_primary_config(&display.layer_id.into(), &image_metadata)?;
435
436 Ok(DisplayResources {
437 frame_set,
438 image_indexes,
439 context,
440 wait_events,
441 busy_images: VecDeque::new(),
442 })
443 }
444
445 async fn maybe_reallocate_display_resources(&mut self) -> Result<(), Error> {
446 if self.display_resources.is_none() {
447 instant!(
448 c"gfx",
449 c"DisplayDirectViewStrategy::allocate_display_resources",
450 fuchsia_trace::Scope::Process,
451 "" => ""
452 );
453 self.collection_id = next_collection_id();
454 self.presented = None;
455 self.display_resources = Some(
456 Self::allocate_display_resources(
457 self.collection_id,
458 self.display.size(),
459 self.display.pixel_format(),
460 self.render_frame_count,
461 &self.display,
462 )
463 .await?,
464 );
465 }
466 Ok(())
467 }
468
469 fn display_resources(&mut self) -> &mut DisplayResources {
470 self.display_resources.as_mut().expect("display_resources")
471 }
472
473 fn update_image(
474 &mut self,
475 view_details: &ViewDetails,
476 view_assistant: &mut ViewAssistantPtr,
477 image: u64,
478 ) {
479 instant!(
480 c"gfx",
481 c"DisplayDirectViewStrategy::update_image",
482 fuchsia_trace::Scope::Process,
483 "image" => format!("{}", image).as_str()
484 );
485 let (event, _) = self.display_resources().wait_events.get(&image).expect("wait event");
486 let buffer_ready_event =
487 event.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate_handle");
488 let direct_context = self.make_context(view_details, Some(image));
489
490 view_assistant
491 .render(&mut self.display_resources().context, buffer_ready_event, &direct_context)
492 .unwrap_or_else(|e| panic!("Update error: {:?}", e));
493 }
494
495 fn handle_vsync_parameters_changed(
496 &mut self,
497 phase: MonotonicInstant,
498 interval: MonotonicDuration,
499 ) {
500 self.vsync_phase = phase;
501 self.vsync_interval = interval;
502 }
503}
504
505#[async_trait(?Send)]
506impl ViewStrategy for DisplayDirectViewStrategy {
507 fn initial_metrics(&self) -> Size {
508 size2(1.0, 1.0)
509 }
510
511 fn initial_physical_size(&self) -> Size {
512 self.display.size().to_f32()
513 }
514
515 fn initial_logical_size(&self) -> Size {
516 self.display.size().to_f32()
517 }
518
519 fn create_view_assistant_context(&self, view_details: &ViewDetails) -> ViewAssistantContext {
520 ViewAssistantContext {
521 key: view_details.key,
522 size: match self.display_rotation {
523 DisplayRotation::Deg0 | DisplayRotation::Deg180 => view_details.physical_size,
524 DisplayRotation::Deg90 | DisplayRotation::Deg270 => {
525 size2(view_details.physical_size.height, view_details.physical_size.width)
526 }
527 },
528 metrics: view_details.metrics,
529 presentation_time: Default::default(),
530 buffer_count: None,
531 image_id: Default::default(),
532 image_index: Default::default(),
533 app_sender: self.app_sender.clone(),
534 mouse_cursor_position: self.mouse_cursor_position.clone(),
535 display_info: Some(DisplayInfo::from(&self.display.info)),
536 }
537 }
538
539 fn setup(&mut self, view_details: &ViewDetails, view_assistant: &mut ViewAssistantPtr) {
540 if let Some(available) = self.display_resources().frame_set.get_available_image() {
541 let direct_context = self.make_context(view_details, Some(available));
542 view_assistant
543 .setup(&direct_context)
544 .unwrap_or_else(|e| panic!("Setup error: {:?}", e));
545 self.display_resources().frame_set.return_image(available);
546 }
547 }
548
549 async fn render(
550 &mut self,
551 view_details: &ViewDetails,
552 view_assistant: &mut ViewAssistantPtr,
553 ) -> bool {
554 duration!("gfx", "DisplayDirectViewStrategy::update");
555 self.maybe_reallocate_display_resources()
556 .await
557 .expect("maybe_reallocate_display_resources");
558 if let Some(available) = self.display_resources().frame_set.get_available_image() {
559 self.update_image(view_details, view_assistant, available);
560 self.display_resources().frame_set.mark_prepared(available);
561 true
562 } else {
563 if self.render_frame_count == 1 {
564 if let Some(presented) = self.presented {
565 self.update_image(view_details, view_assistant, presented);
566 true
567 } else {
568 false
569 }
570 } else {
571 false
572 }
573 }
574 }
575
576 fn present(&mut self, view_details: &ViewDetails) {
577 duration!("gfx", "DisplayDirectViewStrategy::present");
578
579 if self.render_frame_count == 1 && self.presented.is_some() {
580 return;
581 }
582 if let Some(prepared) = self.display_resources().frame_set.prepared {
583 instant!(
584 c"gfx",
585 c"DisplayDirectViewStrategy::present",
586 fuchsia_trace::Scope::Process,
587 "prepared" => format!("{}", prepared).as_str()
588 );
589 let collection_id = self.collection_id;
590 let view_key = view_details.key;
591 self.display
592 .coordinator
593 .set_display_layers(
594 &self.display.display_id.into(),
595 &[self.display.layer_id.into()],
596 )
597 .expect("set_display_layers");
598
599 let (_, wait_event_id) =
600 *self.display_resources().wait_events.get(&prepared).expect("wait event");
601
602 let image_id = DisplayImageId(prepared);
603 self.display
604 .coordinator
605 .set_layer_image2(
606 &self.display.layer_id.into(),
607 &image_id.into(),
608 &wait_event_id.into(),
609 )
610 .expect("Frame::present() set_layer_image2");
611
612 self.last_config_stamp += 1;
613 let stamp = ConfigStamp { value: self.last_config_stamp };
614 let req = CoordinatorCommitConfigRequest { stamp: Some(stamp), ..Default::default() };
615
616 self.display.coordinator.commit_config(req).expect("Frame::present() commit_config");
617
618 self.display_resources().busy_images.push_back(BusyImage {
619 stamp,
620 view_key,
621 image_id,
622 collection_id,
623 });
624
625 self.display_resources().frame_set.mark_presented(prepared);
626 self.presented = Some(prepared);
627 }
628 }
629
630 fn handle_focus(
631 &mut self,
632 view_details: &ViewDetails,
633 view_assistant: &mut ViewAssistantPtr,
634 focus: bool,
635 ) {
636 let mut direct_context = self.make_context(view_details, None);
637 view_assistant
638 .handle_focus_event(&mut direct_context, focus)
639 .unwrap_or_else(|e| panic!("handle_focus error: {:?}", e));
640 }
641
642 fn convert_user_input_message(
643 &mut self,
644 _view_details: &ViewDetails,
645 _message: UserInputMessage,
646 ) -> Result<Vec<crate::input::Event>, Error> {
647 bail!("convert_user_input_message not used for display_direct.")
648 }
649
650 fn inspect_event(&mut self, view_details: &ViewDetails, event: &crate::input::Event) {
651 match &event.event_type {
652 input::EventType::Mouse(mouse_event) => {
653 self.mouse_cursor_position = Some(mouse_event.location);
654 self.app_sender
655 .unbounded_send(MessageInternal::RequestRender(view_details.key))
656 .expect("unbounded_send");
657 }
658 _ => (),
659 };
660 }
661
662 fn image_freed(&mut self, image_id: u64, collection_id: u32) {
663 if BufferCollectionId(collection_id as u64) == self.collection_id {
664 instant!(
665 c"gfx",
666 c"DisplayDirectViewStrategy::image_freed",
667 fuchsia_trace::Scope::Process,
668 "image_freed" => format!("{}", image_id).as_str()
669 );
670 if let Some(display_resources) = self.display_resources.as_mut() {
671 display_resources.frame_set.mark_done_presenting(image_id);
672 }
673 }
674 }
675
676 fn ownership_changed(&mut self, owned: bool) {
677 if !owned {
678 let timer = fasync::Timer::new(fuchsia_async::MonotonicInstant::after(
679 self.display_resource_release_delay.into(),
680 ));
681 let timer_sender = self.app_sender.clone();
682 let task = fasync::Task::local(async move {
683 timer.await;
684 timer_sender
685 .unbounded_send(MessageInternal::DropDisplayResources)
686 .expect("unbounded_send");
687 });
688 self.drop_display_resources_task = Some(task);
689 } else {
690 self.drop_display_resources_task = None;
691 }
692 }
693
694 fn drop_display_resources(&mut self) {
695 let task = self.drop_display_resources_task.take();
696 if task.is_some() {
697 instant!(
698 c"gfx",
699 c"DisplayDirectViewStrategy::drop_display_resources",
700 fuchsia_trace::Scope::Process,
701 "" => ""
702 );
703 self.display_resources = None;
704 }
705 }
706
707 async fn handle_display_coordinator_listener_request(
708 &mut self,
709 event: CoordinatorListenerRequest,
710 ) {
711 match event {
712 CoordinatorListenerRequest::OnVsync {
713 timestamp,
714 cookie,
715 displayed_config_stamp,
716 ..
717 } => {
718 duration!("gfx", "DisplayDirectViewStrategy::OnVsync");
719 let vsync_interval = MonotonicDuration::from_nanos(
720 1_000_000_000_000 / self.display.info.modes[0].refresh_rate_millihertz as i64,
721 );
722 self.handle_vsync_parameters_changed(timestamp, vsync_interval);
723 if cookie.value != INVALID_DISP_ID {
724 self.display
725 .coordinator
726 .acknowledge_vsync(cookie.value)
727 .expect("acknowledge_vsync");
728 }
729
730 let signal_sender = self.app_sender.clone();
731
732 if let Some(display_resources) = self.display_resources.as_mut() {
738 let busy_images = &mut display_resources.busy_images;
739 while !busy_images.is_empty() {
740 let front = &busy_images.front().unwrap();
741 if displayed_config_stamp.value <= front.stamp.value {
742 break;
743 }
744
745 signal_sender
746 .unbounded_send(MessageInternal::ImageFreed(
747 front.view_key,
748 front.image_id.0,
749 front.collection_id.0 as u32,
750 ))
751 .expect("unbounded_send");
752
753 busy_images.pop_front();
754 }
755 }
756
757 signal_sender
758 .unbounded_send(MessageInternal::Render(self.key))
759 .expect("unbounded_send");
760 }
761 CoordinatorListenerRequest::OnDisplaysChanged { .. } => {
762 eprintln!("Carnelian ignoring CoordinatorListenerRequest::OnDisplaysChanged");
763 }
764 CoordinatorListenerRequest::OnClientOwnershipChange { has_ownership, .. } => {
765 eprintln!(
766 "Carnelian ignoring CoordinatorListenerRequest::OnClientOwnershipChange (value: {})",
767 has_ownership
768 );
769 }
770 _ => (),
771 }
772 }
773
774 fn is_hosted_on_display(&self, display_id: DisplayId) -> bool {
775 self.display.display_id == display_id
776 }
777
778 fn close(&mut self) {
779 self.display
780 .coordinator
781 .release_buffer_collection(&self.collection_id.into())
782 .expect("release_buffer_collection");
783 }
784}