Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CGDisplay): add displays_with_point and displays_with_rect #657

Merged
merged 4 commits into from
Aug 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions core-graphics/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<u32, CGError> {
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<CGDirectDisplayID>, 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<CGDirectDisplayID> = 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<u32, CGError> {
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<CGDirectDisplayID>, 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<CGDirectDisplayID> = 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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}