1use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay};
2use core_graphics::{
3 display::{CGDirectDisplayID, CGDisplayBounds, CGGetActiveDisplayList},
4 geometry::{CGPoint, CGRect, CGSize},
5};
6use std::any::Any;
7
8#[derive(Debug)]
9pub struct MacDisplay(pub(crate) CGDirectDisplayID);
10
11unsafe impl Send for MacDisplay {}
12
13impl MacDisplay {
14 /// Get the screen with the given UUID.
15 pub fn find_by_id(id: DisplayId) -> Option<Self> {
16 Self::all().find(|screen| screen.id() == id)
17 }
18
19 /// Get the primary screen - the one with the menu bar, and whose bottom left
20 /// corner is at the origin of the AppKit coordinate system.
21 pub fn primary() -> Self {
22 Self::all().next().unwrap()
23 }
24
25 pub fn all() -> impl Iterator<Item = Self> {
26 unsafe {
27 let mut display_count: u32 = 0;
28 let result = CGGetActiveDisplayList(0, std::ptr::null_mut(), &mut display_count);
29
30 if result == 0 {
31 let mut displays = Vec::with_capacity(display_count as usize);
32 CGGetActiveDisplayList(display_count, displays.as_mut_ptr(), &mut display_count);
33 displays.set_len(display_count as usize);
34
35 displays.into_iter().map(|display| MacDisplay(display))
36 } else {
37 panic!("Failed to get active display list");
38 }
39 }
40 }
41}
42
43/// Convert the given rectangle from CoreGraphics' native coordinate space to GPUI's coordinate space.
44///
45/// CoreGraphics' coordinate space has its origin at the bottom left of the primary screen,
46/// with the Y axis pointing upwards.
47///
48/// Conversely, in GPUI's coordinate system, the origin is placed at the top left of the primary
49/// screen, with the Y axis pointing downwards.
50pub(crate) fn display_bounds_from_native(rect: CGRect) -> Bounds<GlobalPixels> {
51 let primary_screen_size = unsafe { CGDisplayBounds(MacDisplay::primary().id().0) }.size;
52
53 Bounds {
54 origin: point(
55 GlobalPixels(rect.origin.x as f32),
56 GlobalPixels(
57 primary_screen_size.height as f32 - rect.origin.y as f32 - rect.size.height as f32,
58 ),
59 ),
60 size: size(
61 GlobalPixels(rect.size.width as f32),
62 GlobalPixels(rect.size.height as f32),
63 ),
64 }
65}
66
67/// Convert the given rectangle from GPUI's coordinate system to CoreGraphics' native coordinate space.
68///
69/// CoreGraphics' coordinate space has its origin at the bottom left of the primary screen,
70/// with the Y axis pointing upwards.
71///
72/// Conversely, in GPUI's coordinate system, the origin is placed at the top left of the primary
73/// screen, with the Y axis pointing downwards.
74pub(crate) fn display_bounds_to_native(bounds: Bounds<GlobalPixels>) -> CGRect {
75 let primary_screen_height = MacDisplay::primary().bounds().size.height;
76
77 CGRect::new(
78 &CGPoint::new(
79 bounds.origin.x.into(),
80 (primary_screen_height - bounds.origin.y - bounds.size.height).into(),
81 ),
82 &CGSize::new(bounds.size.width.into(), bounds.size.height.into()),
83 )
84}
85
86impl PlatformDisplay for MacDisplay {
87 fn id(&self) -> DisplayId {
88 DisplayId(self.0)
89 }
90
91 fn as_any(&self) -> &dyn Any {
92 self
93 }
94
95 fn bounds(&self) -> Bounds<GlobalPixels> {
96 unsafe {
97 let native_bounds = CGDisplayBounds(self.0);
98 display_bounds_from_native(native_bounds)
99 }
100 }
101}