// ShareableContent APIs - SCShareableContent, SCDisplay, SCWindow, SCRunningApplication
import CoreGraphics
import Foundation
import ScreenCaptureKit
// MARK: - Thread-safe result holder
private class ResultHolder<T> {
private let lock = NSLock()
private var _value: T?
private var _error: String?
var value: T? {
get { lock.lock(); defer { lock.unlock() }; return _value }
set { lock.lock(); defer { lock.unlock() }; _value = newValue }
}
var error: String? {
get { lock.lock(); defer { lock.unlock() }; return _error }
set { lock.lock(); defer { lock.unlock() }; _error = newValue }
}
}
// MARK: - ShareableContent: Content Discovery
/// Copy a Swift String's UTF-8 bytes into a CChar buffer at `offset`. The
/// previous per-byte loop (`for (j, c) in cStr.enumerated()`) was a hot
/// allocation+memcpy that ran twice per window/app in the batch FFI; this
/// helper does the same work with a single `update(from:count:)` (memcpy).
///
/// Returns the number of bytes written **excluding** the NUL terminator, or
/// 0 if the buffer is too small.
private func appendUTF8(
_ string: String,
into buffer: UnsafeMutablePointer<CChar>,
capacity: Int,
offset: inout UInt32
) -> UInt32 {
let bytes = Array(string.utf8)
let len = bytes.count
guard Int(offset) + len <= capacity else {
return 0
}
bytes.withUnsafeBufferPointer { src in
if let baseAddr = src.baseAddress {
buffer
.advanced(by: Int(offset))
.withMemoryRebound(to: UInt8.self, capacity: len) { dest in
dest.update(from: baseAddr, count: len)
}
}
}
offset += UInt32(len)
return UInt32(len)
}
/// Synchronous blocking call to get shareable content
/// Uses DispatchSemaphore to block until async completes
/// Returns content pointer on success, or writes error message to errorBuffer
@_cdecl("sc_shareable_content_get_sync")
public func getShareableContentSync(
excludeDesktopWindows: Bool,
onScreenWindowsOnly: Bool,
errorBuffer: UnsafeMutablePointer<CChar>,
errorBufferSize: Int
) -> OpaquePointer? {
// Force CoreGraphics initialization
initializeCoreGraphics()
let semaphore = DispatchSemaphore(value: 0)
let holder = ResultHolder<SCShareableContent>()
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
excludeDesktopWindows,
onScreenWindowsOnly: onScreenWindowsOnly
)
holder.value = content
} catch {
holder.error = SCBridgeError.contentUnavailable(error.localizedDescription).description
}
semaphore.signal()
}
// Wait with timeout (5 seconds)
let timeout = semaphore.wait(timeout: .now() + 5.0)
if timeout == .timedOut {
"Timeout waiting for shareable content".withCString { ptr in
strncpy(errorBuffer, ptr, errorBufferSize - 1)
errorBuffer[errorBufferSize - 1] = 0
}
return nil
}
if let error = holder.error {
error.withCString { ptr in
strncpy(errorBuffer, ptr, errorBufferSize - 1)
errorBuffer[errorBufferSize - 1] = 0
}
return nil
}
if let content = holder.value {
return retain(content)
}
"Unknown error".withCString { ptr in
strncpy(errorBuffer, ptr, errorBufferSize - 1)
errorBuffer[errorBufferSize - 1] = 0
}
return nil
}
/// Gets shareable content asynchronously
/// - Parameters:
/// - callback: Called with content pointer or error message
/// - userData: User data passed through to callback
@_cdecl("sc_shareable_content_get")
public func getShareableContent(
callback: @escaping @convention(c) (OpaquePointer?, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> Void,
userData: UnsafeMutableRawPointer?
) {
let userDataValue = userData
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
false,
onScreenWindowsOnly: false
)
callback(retain(content), nil, userDataValue)
} catch {
let bridgeError = SCBridgeError.contentUnavailable(error.localizedDescription)
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
}
/// Gets shareable content with options asynchronously
/// - Parameters:
/// - excludeDesktopWindows: Whether to exclude desktop windows
/// - onScreenWindowsOnly: Whether to only include on-screen windows
/// - callback: Called with content pointer or error message
/// - userData: User data passed through to callback
@_cdecl("sc_shareable_content_get_with_options")
public func getShareableContentWithOptions(
excludeDesktopWindows: Bool,
onScreenWindowsOnly: Bool,
callback: @escaping @convention(c) (OpaquePointer?, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> Void,
userData: UnsafeMutableRawPointer?
) {
// Capture userData as a raw value to avoid Sendable issues
let userDataValue = userData
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
excludeDesktopWindows,
onScreenWindowsOnly: onScreenWindowsOnly
)
callback(retain(content), nil, userDataValue)
} catch {
let bridgeError = SCBridgeError.contentUnavailable(error.localizedDescription)
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
}
/// Gets shareable content with windows below a reference window
/// - Parameters:
/// - excludeDesktopWindows: Whether to exclude desktop windows
/// - referenceWindow: The reference window pointer
/// - callback: Called with content pointer or error message
/// - userData: User data passed through to callback
@_cdecl("sc_shareable_content_get_below_window")
public func getShareableContentBelowWindow(
excludeDesktopWindows: Bool,
referenceWindow: OpaquePointer,
callback: @escaping @convention(c) (OpaquePointer?, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> Void,
userData: UnsafeMutableRawPointer?
) {
let userDataValue = userData
let window: SCWindow = unretained(referenceWindow)
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
excludeDesktopWindows,
onScreenWindowsOnlyBelow: window
)
callback(retain(content), nil, userDataValue)
} catch {
let bridgeError = SCBridgeError.contentUnavailable(error.localizedDescription)
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
}
/// Gets shareable content with windows above a reference window
/// - Parameters:
/// - excludeDesktopWindows: Whether to exclude desktop windows
/// - referenceWindow: The reference window pointer
/// - callback: Called with content pointer or error message
/// - userData: User data passed through to callback
@_cdecl("sc_shareable_content_get_above_window")
public func getShareableContentAboveWindow(
excludeDesktopWindows: Bool,
referenceWindow: OpaquePointer,
callback: @escaping @convention(c) (OpaquePointer?, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> Void,
userData: UnsafeMutableRawPointer?
) {
let userDataValue = userData
let window: SCWindow = unretained(referenceWindow)
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
excludeDesktopWindows,
onScreenWindowsOnlyAbove: window
)
callback(retain(content), nil, userDataValue)
} catch {
let bridgeError = SCBridgeError.contentUnavailable(error.localizedDescription)
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
}
#if SCREENCAPTUREKIT_HAS_MACOS15_SDK
/// Gets shareable content for the current process (macOS 14.4+)
/// - Parameters:
/// - callback: Called with content pointer or error message
/// - userData: User data passed through to callback
@_cdecl("sc_shareable_content_get_current_process_displays")
public func getShareableContentCurrentProcessDisplays(
callback: @escaping @convention(c) (OpaquePointer?, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> Void,
userData: UnsafeMutableRawPointer?
) {
let userDataValue = userData
if #available(macOS 14.4, *) {
SCShareableContent.getCurrentProcessShareableContent { content, error in
if let content {
callback(retain(content), nil, userDataValue)
} else {
let bridgeError = SCBridgeError.contentUnavailable(error?.localizedDescription ?? "Unknown error")
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
} else {
// Fallback for older macOS
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
false,
onScreenWindowsOnly: true
)
callback(retain(content), nil, userDataValue)
} catch {
let bridgeError = SCBridgeError.contentUnavailable(error.localizedDescription)
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
}
}
#else
/// Gets shareable content for the current process (fallback for older compilers)
/// - Parameters:
/// - callback: Called with content pointer or error message
/// - userData: User data passed through to callback
@_cdecl("sc_shareable_content_get_current_process_displays")
public func getShareableContentCurrentProcessDisplays(
callback: @escaping @convention(c) (OpaquePointer?, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> Void,
userData: UnsafeMutableRawPointer?
) {
// Fallback for older compilers (macOS < 14.4 SDK)
let userDataValue = userData
Task {
do {
let content = try await SCShareableContent.excludingDesktopWindows(
false,
onScreenWindowsOnly: true
)
callback(retain(content), nil, userDataValue)
} catch {
let bridgeError = SCBridgeError.contentUnavailable(error.localizedDescription)
bridgeError.description.withCString { callback(nil, $0, userDataValue) }
}
}
}
#endif
@_cdecl("sc_shareable_content_retain")
public func retainShareableContent(_ content: OpaquePointer) -> OpaquePointer {
let sc: SCShareableContent = unretained(content)
return retain(sc)
}
@_cdecl("sc_shareable_content_release")
public func releaseShareableContent(_ content: OpaquePointer) {
release(content)
}
@_cdecl("sc_shareable_content_get_displays_count")
public func getShareableContentDisplaysCount(_ content: OpaquePointer) -> Int {
let sc: SCShareableContent = unretained(content)
return sc.displays.count
}
@_cdecl("sc_shareable_content_get_display_at")
public func getShareableContentDisplay(_ content: OpaquePointer, _ index: Int) -> OpaquePointer? {
let sc: SCShareableContent = unretained(content)
guard index >= 0, index < sc.displays.count else { return nil }
return retain(sc.displays[index])
}
@_cdecl("sc_shareable_content_get_windows_count")
public func getShareableContentWindowsCount(_ content: OpaquePointer) -> Int {
let sc: SCShareableContent = unretained(content)
return sc.windows.count
}
@_cdecl("sc_shareable_content_get_window_at")
public func getShareableContentWindow(_ content: OpaquePointer, _ index: Int) -> OpaquePointer? {
let sc: SCShareableContent = unretained(content)
guard index >= 0, index < sc.windows.count else { return nil }
return retain(sc.windows[index])
}
@_cdecl("sc_shareable_content_get_applications_count")
public func getShareableContentApplicationsCount(_ content: OpaquePointer) -> Int {
let sc: SCShareableContent = unretained(content)
return sc.applications.count
}
@_cdecl("sc_shareable_content_get_application_at")
public func getShareableContentApplication(_ content: OpaquePointer, _ index: Int) -> OpaquePointer? {
let sc: SCShareableContent = unretained(content)
guard index >= 0, index < sc.applications.count else { return nil }
return retain(sc.applications[index])
}
// MARK: - SCDisplay
@_cdecl("sc_display_retain")
public func retainDisplay(_ display: OpaquePointer) -> OpaquePointer {
let d: SCDisplay = unretained(display)
return retain(d)
}
@_cdecl("sc_display_release")
public func releaseDisplay(_ display: OpaquePointer) {
release(display)
}
@_cdecl("sc_display_get_display_id")
public func getDisplayId(_ display: OpaquePointer) -> UInt32 {
let d: SCDisplay = unretained(display)
return d.displayID
}
@_cdecl("sc_display_get_width")
public func getDisplayWidth(_ display: OpaquePointer) -> Int {
let d: SCDisplay = unretained(display)
return d.width
}
@_cdecl("sc_display_get_height")
public func getDisplayHeight(_ display: OpaquePointer) -> Int {
let d: SCDisplay = unretained(display)
return d.height
}
@_cdecl("sc_display_get_frame")
public func getDisplayFrame(_ display: OpaquePointer, _ outX: UnsafeMutablePointer<Double>, _ outY: UnsafeMutablePointer<Double>, _ outW: UnsafeMutablePointer<Double>, _ outH: UnsafeMutablePointer<Double>) {
let d: SCDisplay = unretained(display)
let frame = d.frame
outX.pointee = frame.origin.x
outY.pointee = frame.origin.y
outW.pointee = frame.size.width
outH.pointee = frame.size.height
}
// MARK: - SCWindow
@_cdecl("sc_window_retain")
public func retainWindow(_ window: OpaquePointer) -> OpaquePointer {
let w: SCWindow = unretained(window)
return retain(w)
}
@_cdecl("sc_window_release")
public func releaseWindow(_ window: OpaquePointer) {
release(window)
}
@_cdecl("sc_window_get_window_id")
public func getWindowId(_ window: OpaquePointer) -> UInt32 {
let w: SCWindow = unretained(window)
return w.windowID
}
@_cdecl("sc_window_get_title")
public func getWindowTitle(_ window: OpaquePointer, _ buffer: UnsafeMutablePointer<CChar>, _ bufferSize: Int) -> Bool {
let w: SCWindow = unretained(window)
guard let title = w.title, let cString = title.cString(using: .utf8) else {
return false
}
strncpy(buffer, cString, bufferSize - 1)
buffer[bufferSize - 1] = 0
return true
}
@_cdecl("sc_window_get_frame")
public func getWindowFrame(_ window: OpaquePointer, _ outX: UnsafeMutablePointer<Double>, _ outY: UnsafeMutablePointer<Double>, _ outW: UnsafeMutablePointer<Double>, _ outH: UnsafeMutablePointer<Double>) {
let w: SCWindow = unretained(window)
let frame = w.frame
outX.pointee = frame.origin.x
outY.pointee = frame.origin.y
outW.pointee = frame.size.width
outH.pointee = frame.size.height
}
@_cdecl("sc_window_is_on_screen")
public func getWindowIsOnScreen(_ window: OpaquePointer) -> Bool {
let w: SCWindow = unretained(window)
return w.isOnScreen
}
@_cdecl("sc_window_is_active")
public func getWindowIsActive(_ window: OpaquePointer) -> Bool {
let w: SCWindow = unretained(window)
if #available(macOS 13.1, *) { return w.isActive } else { return false }
}
@_cdecl("sc_window_get_window_layer")
public func getWindowLayer(_ window: OpaquePointer) -> Int {
let w: SCWindow = unretained(window)
return w.windowLayer
}
@_cdecl("sc_window_get_owning_application")
public func getWindowOwningApplication(_ window: OpaquePointer) -> OpaquePointer? {
let w: SCWindow = unretained(window)
guard let app = w.owningApplication else { return nil }
return retain(app)
}
// MARK: - SCRunningApplication
@_cdecl("sc_running_application_retain")
public func retainRunningApplication(_ app: OpaquePointer) -> OpaquePointer {
let a: SCRunningApplication = unretained(app)
return retain(a)
}
@_cdecl("sc_running_application_release")
public func releaseRunningApplication(_ app: OpaquePointer) {
release(app)
}
@_cdecl("sc_running_application_get_process_id")
public func getRunningApplicationProcessId(_ app: OpaquePointer) -> Int32 {
let a: SCRunningApplication = unretained(app)
return a.processID
}
@_cdecl("sc_running_application_get_bundle_identifier")
public func getRunningApplicationBundleIdentifier(_ app: OpaquePointer, _ buffer: UnsafeMutablePointer<CChar>, _ bufferSize: Int) -> Bool {
let a: SCRunningApplication = unretained(app)
let bundleId = a.bundleIdentifier; guard let cString = bundleId.cString(using: .utf8) else {
return false
}
strncpy(buffer, cString, bufferSize - 1)
buffer[bufferSize - 1] = 0
return true
}
@_cdecl("sc_running_application_get_application_name")
public func getRunningApplicationName(_ app: OpaquePointer, _ buffer: UnsafeMutablePointer<CChar>, _ bufferSize: Int) -> Bool {
let a: SCRunningApplication = unretained(app)
let name = a.applicationName; guard let cString = name.cString(using: .utf8) else {
return false
}
strncpy(buffer, cString, bufferSize - 1)
buffer[bufferSize - 1] = 0
return true
}
// MARK: - SCShareableContentInfo (macOS 14.0+)
@_cdecl("sc_shareable_content_info_for_filter")
public func getShareableContentInfoForFilter(_ filter: OpaquePointer) -> OpaquePointer? {
let f: SCContentFilter = unretained(filter)
if #available(macOS 14.0, *) {
let info = SCShareableContent.info(for: f)
return retain(info)
}
return nil
}
@_cdecl("sc_shareable_content_info_get_style")
public func getShareableContentInfoStyle(_ info: OpaquePointer) -> Int32 {
if #available(macOS 14.0, *) {
let i: SCShareableContentInfo = unretained(info)
switch i.style {
case .none:
return 0
case .window:
return 1
case .display:
return 2
case .application:
return 3
@unknown default:
return 0
}
}
return 0
}
@_cdecl("sc_shareable_content_info_get_point_pixel_scale")
public func getShareableContentInfoPointPixelScale(_ info: OpaquePointer) -> Float {
if #available(macOS 14.0, *) {
let i: SCShareableContentInfo = unretained(info)
return i.pointPixelScale
}
return 1.0
}
@_cdecl("sc_shareable_content_info_get_content_rect")
public func getShareableContentInfoContentRect(
_ info: OpaquePointer,
_ x: UnsafeMutablePointer<Double>,
_ y: UnsafeMutablePointer<Double>,
_ width: UnsafeMutablePointer<Double>,
_ height: UnsafeMutablePointer<Double>
) {
if #available(macOS 14.0, *) {
let i: SCShareableContentInfo = unretained(info)
let rect = i.contentRect
x.pointee = rect.origin.x
y.pointee = rect.origin.y
width.pointee = rect.size.width
height.pointee = rect.size.height
} else {
x.pointee = 0.0
y.pointee = 0.0
width.pointee = 0.0
height.pointee = 0.0
}
}
@_cdecl("sc_shareable_content_info_retain")
public func retainShareableContentInfo(_ info: OpaquePointer) -> OpaquePointer {
if #available(macOS 14.0, *) {
let i: SCShareableContentInfo = unretained(info)
return retain(i)
}
return info
}
@_cdecl("sc_shareable_content_info_release")
public func releaseShareableContentInfo(_ info: OpaquePointer) {
release(info)
}
// MARK: - Batch Data Retrieval (Optimized FFI)
/// Get all displays as packed data in a single call
/// Returns: number of displays written, or -1 on error
/// The buffer should be pre-allocated by Rust with enough space for maxDisplays * sizeof(FFIDisplayData)
@_cdecl("sc_shareable_content_get_displays_batch")
public func getDisplaysBatch(
_ content: OpaquePointer,
_ rawBuffer: UnsafeMutableRawPointer,
_ maxDisplays: Int
) -> Int {
let buffer = rawBuffer.assumingMemoryBound(to: FFIDisplayData.self)
let sc: SCShareableContent = unretained(content)
let displays = sc.displays
let count = min(displays.count, maxDisplays)
for i in 0 ..< count {
let d = displays[i]
buffer[i] = FFIDisplayData(
displayId: d.displayID,
width: Int32(d.width),
height: Int32(d.height),
frame: FFIRect(d.frame)
)
}
return count
}
/// Get all applications as packed data with strings in a separate buffer
/// Returns: number of applications written
/// stringBuffer receives null-terminated strings packed together
/// stringBufferUsed receives actual bytes used in stringBuffer
@_cdecl("sc_shareable_content_get_applications_batch")
public func getApplicationsBatch(
_ content: OpaquePointer,
_ rawBuffer: UnsafeMutableRawPointer,
_ maxApps: Int,
_ stringBuffer: UnsafeMutablePointer<CChar>,
_ stringBufferSize: Int,
_ stringBufferUsed: UnsafeMutablePointer<Int>
) -> Int {
let buffer = rawBuffer.assumingMemoryBound(to: FFIApplicationData.self)
let sc: SCShareableContent = unretained(content)
let apps = sc.applications
let count = min(apps.count, maxApps)
var stringOffset: UInt32 = 0
for i in 0 ..< count {
let app = apps[i]
let bundleIdStart = stringOffset
let bundleIdLen = appendUTF8(
app.bundleIdentifier,
into: stringBuffer,
capacity: stringBufferSize,
offset: &stringOffset
)
let appNameStart = stringOffset
let appNameLen = appendUTF8(
app.applicationName,
into: stringBuffer,
capacity: stringBufferSize,
offset: &stringOffset
)
buffer[i] = FFIApplicationData(
processId: app.processID,
_padding: 0,
bundleIdOffset: bundleIdStart,
bundleIdLength: bundleIdLen,
appNameOffset: appNameStart,
appNameLength: appNameLen
)
}
stringBufferUsed.pointee = Int(stringOffset)
return count
}
/// Get all windows as packed data with strings in a separate buffer
/// Also provides application pointers for ownership lookup
@_cdecl("sc_shareable_content_get_windows_batch")
public func getWindowsBatch(
_ content: OpaquePointer,
_ rawBuffer: UnsafeMutableRawPointer,
_ maxWindows: Int,
_ stringBuffer: UnsafeMutablePointer<CChar>,
_ stringBufferSize: Int,
_ stringBufferUsed: UnsafeMutablePointer<Int>,
_ appPointers: UnsafeMutablePointer<OpaquePointer?>,
_ maxApps: Int,
_ appCount: UnsafeMutablePointer<Int>
) -> Int {
let buffer = rawBuffer.assumingMemoryBound(to: FFIWindowData.self)
let sc: SCShareableContent = unretained(content)
let windows = sc.windows
let apps = sc.applications
let count = min(windows.count, maxWindows)
var stringOffset: UInt32 = 0
// Build app lookup map and populate app pointers
var appIndexMap: [ObjectIdentifier: Int32] = [:]
let actualAppCount = min(apps.count, maxApps)
for i in 0 ..< actualAppCount {
appIndexMap[ObjectIdentifier(apps[i])] = Int32(i)
appPointers[i] = retain(apps[i])
}
appCount.pointee = actualAppCount
for i in 0 ..< count {
let w = windows[i]
let titleStart = stringOffset
let titleLen: UInt32
if let title = w.title, !title.isEmpty {
titleLen = appendUTF8(
title,
into: stringBuffer,
capacity: stringBufferSize,
offset: &stringOffset
)
} else {
titleLen = 0
}
// Find owning app index
var owningAppIndex: Int32 = -1
if let owningApp = w.owningApplication {
owningAppIndex = appIndexMap[ObjectIdentifier(owningApp)] ?? -1
}
var isActive = false
if #available(macOS 13.1, *) {
isActive = w.isActive
}
buffer[i] = FFIWindowData(
windowId: w.windowID,
windowLayer: Int32(w.windowLayer),
isOnScreen: w.isOnScreen,
isActive: isActive,
frame: FFIRect(w.frame),
titleOffset: titleStart,
titleLength: titleLen,
owningAppIndex: owningAppIndex,
_padding: 0
)
}
stringBufferUsed.pointee = Int(stringOffset)
return count
}
// MARK: - Packed Return Types for Simple Getters
/// Get display frame as packed struct (single call instead of 4 out params)
/// Uses out parameters since Swift @_cdecl can't return structs
@_cdecl("sc_display_get_frame_packed")
public func getDisplayFramePacked(
_ display: OpaquePointer,
_ outX: UnsafeMutablePointer<Double>,
_ outY: UnsafeMutablePointer<Double>,
_ outW: UnsafeMutablePointer<Double>,
_ outH: UnsafeMutablePointer<Double>
) {
let d: SCDisplay = unretained(display)
let frame = d.frame
outX.pointee = frame.origin.x
outY.pointee = frame.origin.y
outW.pointee = frame.size.width
outH.pointee = frame.size.height
}
/// Get window frame as packed struct
@_cdecl("sc_window_get_frame_packed")
public func getWindowFramePacked(
_ window: OpaquePointer,
_ outX: UnsafeMutablePointer<Double>,
_ outY: UnsafeMutablePointer<Double>,
_ outW: UnsafeMutablePointer<Double>,
_ outH: UnsafeMutablePointer<Double>
) {
let w: SCWindow = unretained(window)
let frame = w.frame
outX.pointee = frame.origin.x
outY.pointee = frame.origin.y
outW.pointee = frame.size.width
outH.pointee = frame.size.height
}
/// Get content filter content rect as packed struct (macOS 14.0+)
@_cdecl("sc_content_filter_get_content_rect_packed")
public func getContentFilterContentRectPacked(
_ filter: OpaquePointer,
_ outX: UnsafeMutablePointer<Double>,
_ outY: UnsafeMutablePointer<Double>,
_ outW: UnsafeMutablePointer<Double>,
_ outH: UnsafeMutablePointer<Double>
) {
if #available(macOS 14.0, *) {
let f: SCContentFilter = unretained(filter)
let rect = f.contentRect
outX.pointee = rect.origin.x
outY.pointee = rect.origin.y
outW.pointee = rect.size.width
outH.pointee = rect.size.height
} else {
outX.pointee = 0
outY.pointee = 0
outW.pointee = 0
outH.pointee = 0
}
}
/// Get shareable content info rect as packed struct
@_cdecl("sc_shareable_content_info_get_content_rect_packed")
public func getShareableContentInfoContentRectPacked(
_ info: OpaquePointer,
_ outX: UnsafeMutablePointer<Double>,
_ outY: UnsafeMutablePointer<Double>,
_ outW: UnsafeMutablePointer<Double>,
_ outH: UnsafeMutablePointer<Double>
) {
if #available(macOS 14.0, *) {
let i: SCShareableContentInfo = unretained(info)
let rect = i.contentRect
outX.pointee = rect.origin.x
outY.pointee = rect.origin.y
outW.pointee = rect.size.width
outH.pointee = rect.size.height
} else {
outX.pointee = 0
outY.pointee = 0
outW.pointee = 0
outH.pointee = 0
}
}
// MARK: - Owned String Returns
/// Get window title as owned string (caller must free with sc_free_string)
@_cdecl("sc_window_get_title_owned")
public func getWindowTitleOwned(_ window: OpaquePointer) -> UnsafeMutablePointer<CChar>? {
let w: SCWindow = unretained(window)
guard let title = w.title else { return nil }
return strdup(title)
}
/// Get application bundle identifier as owned string
@_cdecl("sc_running_application_get_bundle_identifier_owned")
public func getRunningApplicationBundleIdentifierOwned(_ app: OpaquePointer) -> UnsafeMutablePointer<CChar>? {
let a: SCRunningApplication = unretained(app)
return strdup(a.bundleIdentifier)
}
/// Get application name as owned string
@_cdecl("sc_running_application_get_application_name_owned")
public func getRunningApplicationNameOwned(_ app: OpaquePointer) -> UnsafeMutablePointer<CChar>? {
let a: SCRunningApplication = unretained(app)
return strdup(a.applicationName)
}
/// Free a string allocated by Swift (strdup)
@_cdecl("sc_free_string")
public func freeString(_ str: UnsafeMutablePointer<CChar>?) {
if let str {
free(str)
}
}