pub mod display;
pub mod running_application;
pub mod window;
pub use display::SCDisplay;
pub use running_application::SCRunningApplication;
pub use window::SCWindow;
use crate::error::SCError;
use crate::utils::completion::{error_from_cstr, SyncCompletion};
use core::fmt;
use std::ffi::c_void;
#[repr(transparent)]
pub struct SCShareableContent(*const c_void);
unsafe impl Send for SCShareableContent {}
unsafe impl Sync for SCShareableContent {}
extern "C" fn shareable_content_callback(
content_ptr: *const c_void,
error_ptr: *const i8,
user_data: *mut c_void,
) {
if !error_ptr.is_null() {
let error = unsafe { error_from_cstr(error_ptr) };
unsafe { SyncCompletion::<SCShareableContent>::complete_err(user_data, error) };
} else if !content_ptr.is_null() {
let content = unsafe { SCShareableContent::from_ptr(content_ptr) };
unsafe { SyncCompletion::complete_ok(user_data, content) };
} else {
unsafe {
SyncCompletion::<SCShareableContent>::complete_err(
user_data,
"Unknown error".to_string(),
);
};
}
}
impl PartialEq for SCShareableContent {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for SCShareableContent {}
impl std::hash::Hash for SCShareableContent {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl Clone for SCShareableContent {
fn clone(&self) -> Self {
unsafe { Self(crate::ffi::sc_shareable_content_retain(self.0)) }
}
}
impl SCShareableContent {
pub(crate) unsafe fn from_ptr(ptr: *const c_void) -> Self {
Self(ptr)
}
pub fn get() -> Result<Self, SCError> {
SCShareableContentOptions::default().get()
}
#[must_use]
pub fn create() -> SCShareableContentOptions {
SCShareableContentOptions::default()
}
pub fn displays(&self) -> Vec<SCDisplay> {
unsafe {
let count = crate::ffi::sc_shareable_content_get_displays_count(self.0);
#[allow(clippy::cast_sign_loss)]
let mut displays = Vec::with_capacity(count as usize);
for i in 0..count {
let display_ptr = crate::ffi::sc_shareable_content_get_display_at(self.0, i);
if !display_ptr.is_null() {
displays.push(SCDisplay::from_ptr(display_ptr));
}
}
displays
}
}
pub fn windows(&self) -> Vec<SCWindow> {
unsafe {
let count = crate::ffi::sc_shareable_content_get_windows_count(self.0);
#[allow(clippy::cast_sign_loss)]
let mut windows = Vec::with_capacity(count as usize);
for i in 0..count {
let window_ptr = crate::ffi::sc_shareable_content_get_window_at(self.0, i);
if !window_ptr.is_null() {
windows.push(SCWindow::from_ptr(window_ptr));
}
}
windows
}
}
pub fn applications(&self) -> Vec<SCRunningApplication> {
unsafe {
let count = crate::ffi::sc_shareable_content_get_applications_count(self.0);
#[allow(clippy::cast_sign_loss)]
let mut apps = Vec::with_capacity(count as usize);
for i in 0..count {
let app_ptr = crate::ffi::sc_shareable_content_get_application_at(self.0, i);
if !app_ptr.is_null() {
apps.push(SCRunningApplication::from_ptr(app_ptr));
}
}
apps
}
}
#[allow(dead_code)]
pub(crate) fn as_ptr(&self) -> *const c_void {
self.0
}
}
impl Drop for SCShareableContent {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
crate::ffi::sc_shareable_content_release(self.0);
}
}
}
}
impl fmt::Debug for SCShareableContent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SCShareableContent")
.field("displays", &self.displays().len())
.field("windows", &self.windows().len())
.field("applications", &self.applications().len())
.finish()
}
}
impl fmt::Display for SCShareableContent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SCShareableContent ({} displays, {} windows, {} applications)",
self.displays().len(),
self.windows().len(),
self.applications().len()
)
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct SCShareableContentOptions {
exclude_desktop_windows: bool,
on_screen_windows_only: bool,
}
impl SCShareableContentOptions {
#[must_use]
pub fn with_exclude_desktop_windows(mut self, exclude: bool) -> Self {
self.exclude_desktop_windows = exclude;
self
}
#[must_use]
pub fn with_on_screen_windows_only(mut self, on_screen_only: bool) -> Self {
self.on_screen_windows_only = on_screen_only;
self
}
#[must_use]
#[deprecated(since = "1.5.0", note = "Use with_exclude_desktop_windows() instead")]
pub fn exclude_desktop_windows(self, exclude: bool) -> Self {
self.with_exclude_desktop_windows(exclude)
}
#[must_use]
#[deprecated(since = "1.5.0", note = "Use with_on_screen_windows_only() instead")]
pub fn on_screen_windows_only(self, on_screen_only: bool) -> Self {
self.with_on_screen_windows_only(on_screen_only)
}
pub fn get(self) -> Result<SCShareableContent, SCError> {
let (completion, context) = SyncCompletion::<SCShareableContent>::new();
unsafe {
crate::ffi::sc_shareable_content_get_with_options(
self.exclude_desktop_windows,
self.on_screen_windows_only,
shareable_content_callback,
context,
);
}
completion.wait().map_err(SCError::NoShareableContent)
}
pub fn below_window(self, reference_window: &SCWindow) -> Result<SCShareableContent, SCError> {
let (completion, context) = SyncCompletion::<SCShareableContent>::new();
unsafe {
crate::ffi::sc_shareable_content_get_below_window(
self.exclude_desktop_windows,
reference_window.as_ptr(),
shareable_content_callback,
context,
);
}
completion.wait().map_err(SCError::NoShareableContent)
}
pub fn above_window(self, reference_window: &SCWindow) -> Result<SCShareableContent, SCError> {
let (completion, context) = SyncCompletion::<SCShareableContent>::new();
unsafe {
crate::ffi::sc_shareable_content_get_above_window(
self.exclude_desktop_windows,
reference_window.as_ptr(),
shareable_content_callback,
context,
);
}
completion.wait().map_err(SCError::NoShareableContent)
}
}
impl SCShareableContent {
#[cfg(feature = "macos_14_4")]
pub fn current_process() -> Result<Self, SCError> {
let (completion, context) = SyncCompletion::<Self>::new();
unsafe {
crate::ffi::sc_shareable_content_get_current_process_displays(
shareable_content_callback,
context,
);
}
completion.wait().map_err(SCError::NoShareableContent)
}
}
#[cfg(feature = "macos_14_0")]
pub struct SCShareableContentInfo(*const c_void);
#[cfg(feature = "macos_14_0")]
impl SCShareableContentInfo {
pub fn for_filter(filter: &crate::stream::content_filter::SCContentFilter) -> Option<Self> {
let ptr = unsafe { crate::ffi::sc_shareable_content_info_for_filter(filter.as_ptr()) };
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
pub fn style(&self) -> crate::stream::content_filter::SCShareableContentStyle {
let value = unsafe { crate::ffi::sc_shareable_content_info_get_style(self.0) };
crate::stream::content_filter::SCShareableContentStyle::from(value)
}
pub fn point_pixel_scale(&self) -> f32 {
unsafe { crate::ffi::sc_shareable_content_info_get_point_pixel_scale(self.0) }
}
pub fn content_rect(&self) -> crate::cg::CGRect {
let mut x = 0.0;
let mut y = 0.0;
let mut width = 0.0;
let mut height = 0.0;
unsafe {
crate::ffi::sc_shareable_content_info_get_content_rect(
self.0,
&mut x,
&mut y,
&mut width,
&mut height,
);
}
crate::cg::CGRect::new(x, y, width, height)
}
pub fn pixel_size(&self) -> (u32, u32) {
let rect = self.content_rect();
let scale = self.point_pixel_scale();
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let width = (rect.width * f64::from(scale)) as u32;
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let height = (rect.height * f64::from(scale)) as u32;
(width, height)
}
}
#[cfg(feature = "macos_14_0")]
impl Drop for SCShareableContentInfo {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
crate::ffi::sc_shareable_content_info_release(self.0);
}
}
}
}
#[cfg(feature = "macos_14_0")]
impl Clone for SCShareableContentInfo {
fn clone(&self) -> Self {
unsafe { Self(crate::ffi::sc_shareable_content_info_retain(self.0)) }
}
}
#[cfg(feature = "macos_14_0")]
impl fmt::Debug for SCShareableContentInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SCShareableContentInfo")
.field("style", &self.style())
.field("point_pixel_scale", &self.point_pixel_scale())
.field("content_rect", &self.content_rect())
.finish()
}
}
#[cfg(feature = "macos_14_0")]
impl fmt::Display for SCShareableContentInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (width, height) = self.pixel_size();
write!(
f,
"ContentInfo({:?}, {}x{} px, scale: {})",
self.style(),
width,
height,
self.point_pixel_scale()
)
}
}
#[cfg(feature = "macos_14_0")]
unsafe impl Send for SCShareableContentInfo {}
#[cfg(feature = "macos_14_0")]
unsafe impl Sync for SCShareableContentInfo {}