display.rs

  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}