src/macosx/native/sun/awt/awt.m
Print this page
*** 40,49 ****
--- 40,52 ----
// The symbol is defined in libosxapp.dylib (ThreadUtilities.m)
extern JavaVM *jvm;
+ // Indicates if AWT is running embedded (in SWT, FX, elsewhere)
+ static BOOL isEmbedded = NO;
+
static bool ShouldPrintVerboseDebugging() {
static int debug = -1;
if (debug == -1) {
debug = (int)(getenv("JAVA_AWT_VERBOSE") != NULL) || (DEBUG != 0);
}
*** 61,77 ****
static void AWT_NSUncaughtExceptionHandler(NSException *exception);
static CFRunLoopObserverRef busyObserver = NULL;
static CFRunLoopObserverRef notBusyObserver = NULL;
! static void setUpAWTAppKit(BOOL swt_mode, BOOL headless) {
! AWT_ASSERT_APPKIT_THREAD;
! BOOL verbose = ShouldPrintVerboseDebugging();
! if (verbose) AWT_DEBUG_LOG(@"setting up busy observers");
!
! JNIEnv *env = [ThreadUtilities getJNIEnv];
!
// Add CFRunLoopObservers to call into AWT so that AWT knows that the
// AWT thread (which is the AppKit main thread) is alive. This way AWT
// will not automatically shutdown.
busyObserver = CFRunLoopObserverCreate(
NULL, // CFAllocator
--- 64,75 ----
static void AWT_NSUncaughtExceptionHandler(NSException *exception);
static CFRunLoopObserverRef busyObserver = NULL;
static CFRunLoopObserverRef notBusyObserver = NULL;
! static void setUpAWTAppKit(JNIEnv *env)
! {
// Add CFRunLoopObservers to call into AWT so that AWT knows that the
// AWT thread (which is the AppKit main thread) is alive. This way AWT
// will not automatically shutdown.
busyObserver = CFRunLoopObserverCreate(
NULL, // CFAllocator
*** 94,111 ****
CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
CFRelease(busyObserver);
CFRelease(notBusyObserver);
! if (!headless) setBusy(YES);
// Set the java name of the AppKit main thread appropriately.
jclass threadClass = NULL;
jstring name = NULL;
jobject curThread = NULL;
- if (!swt_mode) {
threadClass = (*env)->FindClass(env, "java/lang/Thread");
if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;");
if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V");
--- 92,109 ----
CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
CFRelease(busyObserver);
CFRelease(notBusyObserver);
! setBusy(YES);
!
// Set the java name of the AppKit main thread appropriately.
jclass threadClass = NULL;
jstring name = NULL;
jobject curThread = NULL;
threadClass = (*env)->FindClass(env, "java/lang/Thread");
if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;");
if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V");
*** 115,125 ****
if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
name = (*env)->NewStringUTF(env, "AWT-AppKit");
if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
(*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object)
if ((*env)->ExceptionCheck(env)) goto cleanup;
- }
cleanup:
if (threadClass != NULL) {
(*env)->DeleteLocalRef(env, threadClass);
}
--- 113,122 ----
*** 131,164 ****
}
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
-
- // Add the exception handler of last resort
- NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
-
- if (verbose) AWT_DEBUG_LOG(@"finished setting thread name");
}
-
// Returns true if java believes it is running headless
BOOL isHeadless(JNIEnv *env) {
// Just access the property directly, instead of using GraphicsEnvironment.isHeadless.
// This is because this may be called while AWT is being loaded, and calling AWT
// while it is being loaded will deadlock.
static JNF_CLASS_CACHE(jc_Toolkit, "java/awt/GraphicsEnvironment");
static JNF_STATIC_MEMBER_CACHE(jm_isHeadless, jc_Toolkit, "isHeadless", "()Z");
return JNFCallStaticBooleanMethod(env, jm_isHeadless);
}
- BOOL isSWTInWebStart(JNIEnv* env) {
- NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env];
- return [@"true" isCaseInsensitiveLike:swtWebStart];
- }
-
void setBusy(BOOL busy) {
AWT_ASSERT_APPKIT_THREAD;
JNIEnv *env = [ThreadUtilities getJNIEnv];
static JNF_CLASS_CACHE(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");
--- 128,150 ----
*** 198,208 ****
NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
}
// This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used.
@interface AWTStarter : NSObject { }
! + (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart;
- (void)starter:(NSArray*)args;
+ (void)appKitIsRunning:(id)arg;
@end
@implementation AWTStarter
--- 184,194 ----
NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
}
// This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used.
@interface AWTStarter : NSObject { }
! + (void)start:(BOOL)headless;
- (void)starter:(NSArray*)args;
+ (void)appKitIsRunning:(id)arg;
@end
@implementation AWTStarter
*** 240,250 ****
pthread_mutex_unlock(&sAppKitStarted_mutex);
if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started");
}
! + (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart
{
BOOL verbose = ShouldPrintVerboseDebugging();
// Headless: BOTH
// Embedded: BOTH
--- 226,236 ----
pthread_mutex_unlock(&sAppKitStarted_mutex);
if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started");
}
! + (void)start:(BOOL)headless
{
BOOL verbose = ShouldPrintVerboseDebugging();
// Headless: BOTH
// Embedded: BOTH
*** 256,266 ****
// onMainThread here will be false but SWT mode will be true. If we are currently on the main thread, we don't
// need to throw AWT startup over to another thread.
BOOL onMainThread = (pthread_main_np() != 0);
if (verbose) {
! NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d swtMode:%d swtModeForWebStart:%d] { onMainThread:%d }", headless, swtMode, swtModeForWebStart, onMainThread];
AWT_DEBUG_LOG(msg);
}
if (!headless)
{
--- 242,252 ----
// onMainThread here will be false but SWT mode will be true. If we are currently on the main thread, we don't
// need to throw AWT startup over to another thread.
BOOL onMainThread = (pthread_main_np() != 0);
if (verbose) {
! NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread];
AWT_DEBUG_LOG(msg);
}
if (!headless)
{
*** 278,290 ****
id st = [[AWTStarter alloc] init];
NSArray * args = [NSArray arrayWithObjects:
[NSNumber numberWithBool: onMainThread],
- [NSNumber numberWithBool: swtMode],
[NSNumber numberWithBool: headless],
- [NSNumber numberWithBool: swtModeForWebStart],
[NSNumber numberWithBool: verbose],
nil];
if (onMainThread) {
[st starter:args];
--- 264,274 ----
*** 322,378 ****
- (void)starter:(NSArray*)args {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
BOOL onMainThread = [[args objectAtIndex:0] boolValue];
! BOOL swtMode = [[args objectAtIndex:1] boolValue];
! BOOL headless = [[args objectAtIndex:2] boolValue];
! BOOL swtModeForWebStart = [[args objectAtIndex:3] boolValue];
! BOOL verbose = [[args objectAtIndex:4] boolValue];
BOOL wasOnMainThread = onMainThread;
! setUpAWTAppKit(swtMode, headless);
// Headless mode trumps either ordinary AWT or SWT-in-AWT mode. Declare us a daemon and return.
if (headless) {
BOOL didBecomeDaemon = [AWTStarter markAppAsDaemon];
return;
}
- if (swtMode || swtModeForWebStart) {
- if (verbose) NSLog(@"in SWT or SWT/WebStart mode");
-
- // The SWT should call NSApplicationLoad, but they don't know a priori that they will be using the AWT, so they don't.
- NSApplicationLoad();
- }
-
// This will create a NSApplicationAWT for standalone AWT programs, unless there is
// already a NSApplication instance. If there is already a NSApplication instance,
// and -[NSApplication isRunning] returns YES, AWT is embedded inside another
// AppKit Application.
NSApplication *app = [NSApplicationAWT sharedApplication];
// AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
if (![app isRunning]) {
if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
// This is where the AWT AppKit thread parks itself to process events.
[NSApplicationAWT runAWTLoopWithApp: app];
} else {
// We're either embedded, or showing a splash screen
! if (![NSApp isKindOfClass:[NSApplicationAWT class]]) {
if (verbose) AWT_DEBUG_LOG(@"running embedded");
- // Since we're embedded, no need to be swamping the runloop with the observers.
- CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
- CFRunLoopRemoveObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
- CFRunLoopRemoveObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
// We don't track if the runloop is busy, so set it free to let AWT finish when it needs
setBusy(NO);
- busyObserver = NULL;
- notBusyObserver = NULL;
} else {
if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen");
}
// Signal so that JNI_OnLoad can proceed.
--- 306,372 ----
- (void)starter:(NSArray*)args {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
BOOL onMainThread = [[args objectAtIndex:0] boolValue];
! BOOL headless = [[args objectAtIndex:1] boolValue];
! BOOL verbose = [[args objectAtIndex:2] boolValue];
BOOL wasOnMainThread = onMainThread;
! // Add the exception handler of last resort
! NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
// Headless mode trumps either ordinary AWT or SWT-in-AWT mode. Declare us a daemon and return.
if (headless) {
BOOL didBecomeDaemon = [AWTStarter markAppAsDaemon];
return;
}
// This will create a NSApplicationAWT for standalone AWT programs, unless there is
// already a NSApplication instance. If there is already a NSApplication instance,
// and -[NSApplication isRunning] returns YES, AWT is embedded inside another
// AppKit Application.
NSApplication *app = [NSApplicationAWT sharedApplication];
+ isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ if (isEmbedded) {
+ static JNF_CLASS_CACHE(jc_AWTKeepAlive, "sun/lwawt/macosx/AWTKeepAlive");
+ static JNF_STATIC_MEMBER_CACHE(jm_activate, jc_AWTKeepAlive, "activate", "()V");
+
+ // This will send native events every 0.5 s, thus letting the embedder know
+ // that AWT is alive. The events are only sent while there are active
+ // AWT windows present.
+ JNFCallStaticVoidMethod(env, jm_activate);
+ } else {
+ // Install run loop observers and set the AppKit Java thread name
+ setUpAWTAppKit(env);
+ }
// AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
if (![app isRunning]) {
if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
+ // Note that in theory we can get here even if we're embedded and an
+ // embedder isn't running an event loop yet for some reason. However,
+ // it seems to be very unlikely. In any case, the runAWTLoopWithApp is
+ // able to deal with it anyway. Whether the embedder can deal with
+ // this is unknown. Also, it's unclear how the event loop is going to
+ // be terminated in this case. Setting up observers is not an option
+ // since the embedder could force termination. We wouldn't know and
+ // could crash when calling to Java.
+
// This is where the AWT AppKit thread parks itself to process events.
[NSApplicationAWT runAWTLoopWithApp: app];
} else {
// We're either embedded, or showing a splash screen
! if (isEmbedded) {
if (verbose) AWT_DEBUG_LOG(@"running embedded");
// We don't track if the runloop is busy, so set it free to let AWT finish when it needs
setBusy(NO);
} else {
if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen");
}
// Signal so that JNI_OnLoad can proceed.
*** 406,458 ****
if (status != JNI_OK || env == NULL) {
AWT_DEBUG_LOG(@"Can't get JNIEnv");
return JNI_VERSION_1_4;
}
- // The following is true when AWT is attempting to connect to the window server
- // when it isn't set up properly to do so.
- // BOOL AWTLoadFailure = YES; For now we are skipping this check so i'm commenting out this variable as unused
JNF_COCOA_ENTER(env);
- // If -XstartOnFirstThread was used at invocation time, an environment variable will be set.
- // (See java_md.c for the matching setenv call.) When that happens, we assume the SWT will be in use.
- BOOL swt_compatible_mode = NO;
-
char envVar[80];
snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
if (getenv(envVar) != NULL) {
! swt_compatible_mode = YES;
unsetenv(envVar);
}
- BOOL swt_in_webstart = isSWTInWebStart(env);
BOOL headless = isHeadless(env);
- // Make sure we're on the right thread. If not, we still need the JNIEnv to throw an exception.
- if (pthread_main_np() != 0 && !swt_compatible_mode && !headless) {
- AWT_DEBUG_LOG(@"Apple AWT Java VM was loaded on first thread -- can't start AWT.");
- [JNFException raise:env as:kInternalError reason:"Can't start the AWT because Java was started on the first thread. Make sure StartOnFirstThread is "
- "not specified in your application's Info.plist or on the command line"];
- return JNI_VERSION_1_4;
- }
-
// We need to let Foundation know that this is a multithreaded application, if it isn't already.
if (![NSThread isMultiThreaded]) {
[NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
}
! // if (swt_compatible_mode || headless || [AWTStarter isConnectedToWindowServer] || [AWTStarter isRemoteSession]) {
! // No need in this check - we will try to launch AWTStarter anyways - to be able to run GUI application remotely
! // AWTLoadFailure = NO;
!
! [AWTStarter start:headless swtMode:swt_compatible_mode swtModeForWebStart:swt_in_webstart];
!
! // }
!
! /* if (AWTLoadFailure) { // We will not reach this code anyways
! [JNFException raise:env as:kInternalError reason:"Can't connect to window server - not enough permissions."];
! } */
JNF_COCOA_EXIT(env);
if (verbose) AWT_DEBUG_LOG(@"exiting JNI_OnLoad");
--- 400,427 ----
if (status != JNI_OK || env == NULL) {
AWT_DEBUG_LOG(@"Can't get JNIEnv");
return JNI_VERSION_1_4;
}
JNF_COCOA_ENTER(env);
char envVar[80];
snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
if (getenv(envVar) != NULL) {
! // If we don't need this in the long term, it makes sense to not set
! // this env variable in the launcher code in the first place
unsetenv(envVar);
}
BOOL headless = isHeadless(env);
// We need to let Foundation know that this is a multithreaded application, if it isn't already.
if (![NSThread isMultiThreaded]) {
[NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
}
! [AWTStarter start:headless];
JNF_COCOA_EXIT(env);
if (verbose) AWT_DEBUG_LOG(@"exiting JNI_OnLoad");