Skip to content
Merged
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions scripts/desktop-tauri-build.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ test('Desktop packaging selects the Web font profile from its target triple', ()
);
});

test('macOS capture bridge avoids unresolved availability compiler runtime helpers', () => {
const source = readFileSync(join(ROOT, 'src', 'apps', 'desktop', 'src', 'computer_use', 'macos_capture.m'), 'utf8');
assert.match(source, /static BOOL obf_os_at_least\(/);
assert.doesNotMatch(source, /@available\(macOS/);
});

test('release builds do not mutate DMGs after Tauri signs and notarizes them', () => {
const source = readFileSync(join(ROOT, 'scripts', 'desktop-tauri-build.mjs'), 'utf8');
assert.doesNotMatch(source, /patchDmgExtras/);
Expand Down
16 changes: 13 additions & 3 deletions src/apps/desktop/src/computer_use/macos_capture.m
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ static void obf_error(char *buffer, size_t capacity, NSString *message) {
if (buffer && capacity) snprintf(buffer, capacity, "%s", message.UTF8String ?: "Native capture error");
}

// Avoid Objective-C availability syntax here. Rust links this bridge with
// `-nodefaultlibs`, so clang's compiler-rt availability helper would remain
// unresolved in the final desktop binary. NSProcessInfo is available on our
// minimum deployment target and preserves the same runtime guard semantics.
static BOOL obf_os_at_least(NSInteger major, NSInteger minor) {
NSOperatingSystemVersion version = NSProcessInfo.processInfo.operatingSystemVersion;
return version.majorVersion > major ||
(version.majorVersion == major && version.minorVersion >= minor);
}

// The lock fact is present on current macOS releases but is not a required
// dictionary key. Missing metadata is unknown, never proof that a session is
// locked; preserve ordinary capture errors in that case.
Expand Down Expand Up @@ -208,7 +218,7 @@ uint32_t obf_capture_validate_target(int32_t pid, uint32_t requestedWindow, char
void *obf_capture_start(int32_t pid, uint32_t requestedWindow, uint64_t generation, char *error, size_t errorCapacity) {
obf_install_activation_observer();
@autoreleasepool {
if (@available(macOS 12.3, *)) {
if (obf_os_at_least(12, 3)) {
if ([NSThread isMainThread]) {
obf_error(error, errorCapacity, @"CAPTURE_WRONG_THREAD: Start capture on a worker thread");
return NULL;
Expand Down Expand Up @@ -242,8 +252,8 @@ uint32_t obf_capture_validate_target(int32_t pid, uint32_t requestedWindow, char
config.queueDepth = 3;
config.showsCursor = NO;
config.pixelFormat = kCVPixelFormatType_32BGRA;
if (@available(macOS 13.0, *)) config.capturesAudio = NO;
if (@available(macOS 14.0, *)) config.ignoreShadowsSingleWindow = YES;
if (obf_os_at_least(13, 0)) config.capturesAudio = NO;
if (obf_os_at_least(14, 0)) config.ignoreShadowsSingleWindow = YES;
SCStream *stream = [[SCStream alloc] initWithFilter:filter configuration:config delegate:session];
session.stream = stream;
session.configuration = config;
Expand Down
Loading