From d7b6414818603e139004dce634edbac9d808907b Mon Sep 17 00:00:00 2001 From: Karl von Randow Date: Thu, 26 Mar 2026 08:53:29 +1300 Subject: [PATCH] Fix thread safety: dispatch native AppKit/NSUserDefaults reads to main thread nativeGetApplicationAppearanceName was reading NSApp.appearance directly from the JNI calling thread. nativeGetAppearanceSettings was reading NSUserDefaults and NSWorkspace accessibility properties off the main thread. Both now use runOnMainThread to safely access these APIs. --- libvappearances/VAppearances.m | 47 +++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/libvappearances/VAppearances.m b/libvappearances/VAppearances.m index d3ce586..a953f8d 100644 --- a/libvappearances/VAppearances.m +++ b/libvappearances/VAppearances.m @@ -387,47 +387,54 @@ static void registerDefaultColor(NSMutableString *stream, NSString *name, CGFloa jsize intCount = (*env)->GetArrayLength(env, jints); jsize objectCount = (*env)->GetArrayLength(env, jobjects); - int *data = (*env)->GetIntArrayElements(env, jints, NULL); + + // Read all native values on the main thread + __block NSString *appearanceName = nil; + __block NSString *hcs = nil; + __block NSInteger accentValue = -2; + __block NSInteger tintedOption = 0; + __block BOOL increaseContrastOption = NO; + __block BOOL reduceTransparencyOption = NO; + + runOnMainThread(^() { + appearanceName = [getAppearanceName(getCurrentAppearanceMainThread()) retain]; + hcs = [[NSUserDefaults.standardUserDefaults stringForKey:@"AppleHighlightColor"] retain]; + id acv = [NSUserDefaults.standardUserDefaults objectForKey:@"AppleAccentColor"]; + accentValue = (acv != nil) ? [acv integerValue] : -2; + tintedOption = [NSUserDefaults.standardUserDefaults integerForKey:@"NSGlassDiffusionSetting"]; + increaseContrastOption = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldIncreaseContrast; + reduceTransparencyOption = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceTransparency; + }); if (objectCount > 0) { - NSString *appearanceName = getAppearanceName(getCurrentAppearance()); (*env)->SetObjectArrayElement(env, jobjects, 0, TO_JAVA_STRING(appearanceName)); } - if (objectCount > 1) { - NSString *hcs = [NSUserDefaults.standardUserDefaults stringForKey:@"AppleHighlightColor"]; (*env)->SetObjectArrayElement(env, jobjects, 1, TO_JAVA_STRING(hcs)); } + [appearanceName release]; + [hcs release]; + int *data = (*env)->GetIntArrayElements(env, jints, NULL); if (data != NULL) { - if (intCount > 0) { - id acv = [NSUserDefaults.standardUserDefaults objectForKey:@"AppleAccentColor"]; - // map null (multicolor) to -2 - data[0] = (acv != nil) ? [acv integerValue] : -2; + data[0] = accentValue; } - if (intCount > 1) { - NSInteger tintedOption = [NSUserDefaults.standardUserDefaults integerForKey:@"NSGlassDiffusionSetting"]; data[1] = tintedOption; } - if (intCount > 2) { - BOOL increaseContrastOption = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldIncreaseContrast; if (DEBUG_FLAG) { NSLog(@"Increase contrast option: %@", increaseContrastOption ? @"YES" : @"NO"); } data[2] = increaseContrastOption; } - if (intCount > 3) { - BOOL reduceTransparencyOption = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceTransparency; if (DEBUG_FLAG) { NSLog(@"Reduce transparency option: %@", reduceTransparencyOption ? @"YES" : @"NO"); } data[3] = reduceTransparencyOption; } - (*env)->ReleaseIntArrayElements(env, jints, data, 0); result = 0; } @@ -617,13 +624,17 @@ static void registerListeners(JNIEnv *env, jobject settingsListener, jobject eff COCOA_ENTER(); - NSAppearanceName appearanceName; + __block NSAppearanceName appearanceName; if (@available(macOS 10.14, *)) { - appearanceName = [NSApp.appearance name]; + runOnMainThread(^() { + appearanceName = [[NSApp.appearance name] retain]; + }); + result = TO_JAVA_STRING(appearanceName); + [appearanceName release]; } else { appearanceName = NSAppearanceNameAqua; + result = TO_JAVA_STRING(appearanceName); } - result = TO_JAVA_STRING(appearanceName); COCOA_EXIT();