Skip to content

Commit ed48495

Browse files
committed
fix(tests): initialize CoreGraphics for headless CI environments
Add sc_initialize_core_graphics() FFI function that calls CGMainDisplayID() to prevent CGS_REQUIRE_INIT assertion crashes on headless systems. Called via cg_init_for_headless_ci() in content_filter_tests.
1 parent 4961d68 commit ed48495

4 files changed

Lines changed: 38 additions & 7 deletions

File tree

src/ffi/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
//! Swift FFI bridge to `ScreenCaptureKit`
22
use std::ffi::c_void;
33

4+
// MARK: - CoreGraphics Initialization
5+
extern "C" {
6+
/// Force CoreGraphics initialization by calling CGMainDisplayID
7+
/// This prevents CGS_REQUIRE_INIT crashes on headless systems
8+
pub fn sc_initialize_core_graphics();
9+
}
10+
411
// MARK: - SCShareableContent
512
extern "C" {
613
/// Synchronous blocking call to get shareable content

swift-bridge/Sources/ScreenCaptureKitBridge/Core.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
// Core memory management utilities for the Swift bridge
22

3+
import CoreGraphics
34
import Foundation
45

6+
// MARK: - CoreGraphics Initialization
7+
8+
/// Force CoreGraphics initialization by calling CGMainDisplayID
9+
/// This prevents CGS_REQUIRE_INIT crashes on headless systems
10+
/// Made public so it can be called from Rust FFI
11+
@_cdecl("sc_initialize_core_graphics")
12+
public func initializeCoreGraphics() {
13+
_ = CGMainDisplayID()
14+
}
15+
516
// MARK: - Error Types
617

718
/// Strongly typed errors for the ScreenCaptureKit bridge

swift-bridge/Sources/ScreenCaptureKitBridge/ShareableContent.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import CoreGraphics
44
import Foundation
55
import ScreenCaptureKit
66

7-
// MARK: - CoreGraphics Initialization
87

9-
/// Force CoreGraphics initialization by calling CGMainDisplayID
10-
/// This prevents CGS_REQUIRE_INIT crashes on headless systems
11-
private func ensureCoreGraphicsInitialized() {
12-
_ = CGMainDisplayID()
13-
}
148

159
// MARK: - Thread-safe result holder
1610

@@ -43,7 +37,7 @@ public func getShareableContentSync(
4337
errorBufferSize: Int
4438
) -> OpaquePointer? {
4539
// Force CoreGraphics initialization
46-
ensureCoreGraphicsInitialized()
40+
initializeCoreGraphics()
4741

4842
let semaphore = DispatchSemaphore(value: 0)
4943
let holder = ResultHolder<SCShareableContent>()

tests/content_filter_tests.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,17 @@ use screencapturekit::shareable_content::SCShareableContent;
44
use screencapturekit::stream::configuration::{Point, Rect, Size};
55
use screencapturekit::stream::content_filter::SCContentFilter;
66

7+
// Initialize CoreGraphics to prevent CGS_REQUIRE_INIT crashes in CI
8+
fn cg_init_for_headless_ci() {
9+
extern "C" {
10+
fn sc_initialize_core_graphics();
11+
}
12+
unsafe { sc_initialize_core_graphics() }
13+
}
14+
715
#[test]
816
fn test_content_filter_builder_display() {
17+
cg_init_for_headless_ci();
918
let content = SCShareableContent::get().expect("Failed to get shareable content");
1019
let display = &content.displays()[0];
1120

@@ -21,6 +30,7 @@ fn test_content_filter_builder_display() {
2130

2231
#[test]
2332
fn test_content_filter_builder_window() {
33+
cg_init_for_headless_ci();
2434
let content = SCShareableContent::get().expect("Failed to get shareable content");
2535

2636
if let Some(window) = content.windows().first() {
@@ -32,6 +42,7 @@ fn test_content_filter_builder_window() {
3242

3343
#[test]
3444
fn test_content_filter_exclude_windows() {
45+
cg_init_for_headless_ci();
3546
let content = SCShareableContent::get().expect("Failed to get shareable content");
3647
let display = &content.displays()[0];
3748
let windows = content.windows();
@@ -50,6 +61,7 @@ fn test_content_filter_exclude_windows() {
5061

5162
#[test]
5263
fn test_content_filter_include_windows() {
64+
cg_init_for_headless_ci();
5365
let content = SCShareableContent::get().expect("Failed to get shareable content");
5466
let display = &content.displays()[0];
5567
let windows = content.windows();
@@ -68,6 +80,7 @@ fn test_content_filter_include_windows() {
6880

6981
#[test]
7082
fn test_content_filter_include_applications() {
83+
cg_init_for_headless_ci();
7184
let content = SCShareableContent::get().expect("Failed to get shareable content");
7285
let display = &content.displays()[0];
7386
let apps = content.applications();
@@ -86,6 +99,7 @@ fn test_content_filter_include_applications() {
8699

87100
#[test]
88101
fn test_content_filter_content_rect() {
102+
cg_init_for_headless_ci();
89103
let content = SCShareableContent::get().expect("Failed to get shareable content");
90104
let display = &content.displays()[0];
91105

@@ -106,6 +120,7 @@ fn test_content_filter_content_rect() {
106120

107121
#[test]
108122
fn test_content_filter_set_content_rect() {
123+
cg_init_for_headless_ci();
109124
let content = SCShareableContent::get().expect("Failed to get shareable content");
110125
let display = &content.displays()[0];
111126

@@ -123,6 +138,7 @@ fn test_content_filter_set_content_rect() {
123138

124139
#[test]
125140
fn test_content_filter_clone() {
141+
cg_init_for_headless_ci();
126142
let content = SCShareableContent::get().expect("Failed to get shareable content");
127143
let display = &content.displays()[0];
128144

@@ -146,6 +162,7 @@ fn test_content_filter_send_sync() {
146162

147163
#[test]
148164
fn test_content_filter_debug_display() {
165+
cg_init_for_headless_ci();
149166
let content = SCShareableContent::get().expect("Failed to get shareable content");
150167
let display = &content.displays()[0];
151168

@@ -163,6 +180,7 @@ fn test_content_filter_debug_display() {
163180

164181
#[test]
165182
fn test_content_filter_equality() {
183+
cg_init_for_headless_ci();
166184
let content = SCShareableContent::get().expect("Failed to get shareable content");
167185
let display = &content.displays()[0];
168186

@@ -181,6 +199,7 @@ fn test_content_filter_equality() {
181199

182200
#[test]
183201
fn test_content_filter_hash() {
202+
cg_init_for_headless_ci();
184203
use std::collections::HashSet;
185204

186205
let content = SCShareableContent::get().expect("Failed to get shareable content");

0 commit comments

Comments
 (0)