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