diff --git a/core-graphics/src/display.rs b/core-graphics/src/display.rs index 4095f16..2e40f77 100644 --- a/core-graphics/src/display.rs +++ b/core-graphics/src/display.rs @@ -20,6 +20,7 @@ pub use crate::geometry::{CGPoint, CGRect, CGSize}; use crate::image::CGImage; use core_foundation::base::{CFRetain, TCFType}; use core_foundation::string::{CFString, CFStringRef}; +use core_graphics_types::base::kCGErrorSuccess; use foreign_types::{foreign_type, ForeignType}; pub type CGDirectDisplayID = u32; @@ -177,6 +178,90 @@ impl CGDisplay { CGDisplay::new(kCGNullDirectDisplayID) } + /// Return the number of online displays with bounds that include the + /// specified point. + #[inline] + pub fn display_count_with_point(point: CGPoint) -> Result { + let mut matching_display_count: u32 = 0; + let result = unsafe { + CGGetDisplaysWithPoint(point, 0, ptr::null_mut(), &mut matching_display_count) + }; + if result == kCGErrorSuccess { + Ok(matching_display_count) + } else { + Err(result) + } + } + + /// Return a list of online displays with bounds that include the specified + /// point. + #[inline] + pub fn displays_with_point( + point: CGPoint, + max_displays: u32, + ) -> Result<(Vec, u32), CGError> { + let count = CGDisplay::display_count_with_point(point)?; + let count = u32::max(u32::min(count, max_displays), 1); + + let mut matching_display_count: u32 = 0; + let mut displays: Vec = vec![0; count as usize]; + let result = unsafe { + CGGetDisplaysWithPoint( + point, + max_displays, + displays.as_mut_ptr(), + &mut matching_display_count, + ) + }; + + if result == kCGErrorSuccess { + Ok((displays, matching_display_count)) + } else { + Err(result) + } + } + + /// Return the number of online displays with bounds that intersect the + /// specified rectangle. + #[inline] + pub fn display_count_with_rect(rect: CGRect) -> Result { + let mut matching_display_count: u32 = 0; + let result = + unsafe { CGGetDisplaysWithRect(rect, 0, ptr::null_mut(), &mut matching_display_count) }; + if result == kCGErrorSuccess { + Ok(matching_display_count) + } else { + Err(result) + } + } + + /// Return a list of online displays with bounds that intersect the specified rectangle. + #[inline] + pub fn displays_with_rect( + rect: CGRect, + max_displays: u32, + ) -> Result<(Vec, u32), CGError> { + let count = CGDisplay::display_count_with_rect(rect)?; + let count = u32::max(u32::min(count, max_displays), 1); + + let mut matching_display_count: u32 = 0; + let mut displays: Vec = vec![0; count as usize]; + let result = unsafe { + CGGetDisplaysWithRect( + rect, + max_displays, + displays.as_mut_ptr(), + &mut matching_display_count, + ) + }; + + if result == kCGErrorSuccess { + Ok((displays, matching_display_count)) + } else { + Err(result) + } + } + /// Returns the bounds of a display in the global display coordinate space. #[inline] pub fn bounds(&self) -> CGRect { @@ -714,6 +799,12 @@ extern "C" { active_displays: *mut CGDirectDisplayID, display_count: *mut u32, ) -> CGError; + pub fn CGGetDisplaysWithPoint( + point: CGPoint, + max_displays: u32, + displays: *mut CGDirectDisplayID, + matching_display_count: *mut u32, + ) -> CGError; pub fn CGGetDisplaysWithRect( rect: CGRect, max_displays: u32, @@ -839,3 +930,71 @@ extern "C" { imageOptions: CGWindowImageOption, ) -> crate::sys::CGImageRef; } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_display_count_with_point() { + let result = CGDisplay::display_count_with_point(CGPoint::new(0., 0.)); + assert!(result.is_ok()); + } + + #[test] + fn test_displays_with_point_0() { + let result = CGDisplay::displays_with_point(CGPoint::new(0., 0.), 0); + assert!(result.is_ok()); + let (displays, count) = result.unwrap(); + assert_eq!(displays.len(), count as usize); + } + + #[test] + fn test_displays_with_point_5() { + let result = CGDisplay::displays_with_point(CGPoint::new(0., 0.), 5); + assert!(result.is_ok()); + let (displays, count) = result.unwrap(); + assert_eq!(displays.len(), count as usize); + } + + // NOTE: CGMainDisplayID must be called before CGGetDisplaysWithRect to avoid: + // Assertion failed: (did_initialize), function CGS_REQUIRE_INIT, file CGInitialization.c, line 44. + // See https://github.com/JXA-Cookbook/JXA-Cookbook/issues/27#issuecomment-277517668 + + #[test] + fn test_display_count_with_rect() { + let _ = CGDisplay::main(); + + let result = CGDisplay::display_count_with_rect(CGRect::new( + &CGPoint::new(10., 10.), + &CGSize::new(100., 100.), + )); + assert!(result.is_ok()); + } + + #[test] + fn test_displays_with_rect_0() { + let _ = CGDisplay::main(); + + let result = CGDisplay::displays_with_rect( + CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(100., 100.)), + 0, + ); + assert!(result.is_ok()); + let (displays, count) = result.unwrap(); + assert_eq!(displays.len(), count as usize); + } + + #[test] + fn test_displays_with_rect_5() { + let _ = CGDisplay::main(); + + let result = CGDisplay::displays_with_rect( + CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(100., 100.)), + 5, + ); + assert!(result.is_ok()); + let (displays, count) = result.unwrap(); + assert_eq!(displays.len(), count as usize); + } +}