1 /*
   2  * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import <dlfcn.h>
  27 #import <pthread.h>
  28 #import <objc/runtime.h>
  29 #import <Cocoa/Cocoa.h>
  30 #import <Security/AuthSession.h>
  31 
  32 #include "jni_util.h"
  33 #import "LWCToolkit.h"
  34 #import "ThreadUtilities.h"
  35 #import "CSystemColors.h"
  36 #import  "NSApplicationAWT.h"
  37 #import "PropertiesUtilities.h"
  38 #import "ApplicationDelegate.h"
  39 
  40 #import "sun_lwawt_macosx_LWCToolkit.h"
  41 
  42 #import "sizecalc.h"
  43 
  44 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
  45 
  46 // SCROLL PHASE STATE
  47 #define SCROLL_PHASE_UNSUPPORTED 1
  48 #define SCROLL_PHASE_BEGAN 2
  49 #define SCROLL_PHASE_CONTINUED 3
  50 #define SCROLL_PHASE_MOMENTUM_BEGAN 4
  51 #define SCROLL_PHASE_ENDED 5
  52 
  53 int gNumberOfButtons;
  54 jint* gButtonDownMasks;
  55 
  56 // Indicates that the app has been started with -XstartOnFirstThread
  57 // (directly or via WebStart settings), and AWT should not run its
  58 // own event loop in this mode. Even if a loop isn't running yet,
  59 // we expect an embedder (e.g. SWT) to start it some time later.
  60 static BOOL forceEmbeddedMode = NO;
  61 
  62 // Indicates if awt toolkit is embedded into another UI toolkit
  63 static BOOL isEmbedded = NO;
  64 
  65 // This is the data necessary to have JNI_OnLoad wait for AppKit to start.
  66 static BOOL sAppKitStarted = NO;
  67 static pthread_mutex_t sAppKitStarted_mutex = PTHREAD_MUTEX_INITIALIZER;
  68 static pthread_cond_t sAppKitStarted_cv = PTHREAD_COND_INITIALIZER;
  69 
  70 @implementation AWTToolkit
  71 
  72 static long eventCount;
  73 static BOOL inDoDragDropLoop;
  74 
  75 + (BOOL) inDoDragDropLoop {
  76   @synchronized(self) {
  77     return inDoDragDropLoop;
  78   }
  79 }
  80 
  81 + (void) setInDoDragDropLoop:(BOOL)val {
  82   @synchronized(self) {
  83     inDoDragDropLoop = val;
  84   }
  85 }
  86 
  87 + (long) getEventCount{
  88   @synchronized(self) {
  89     return eventCount;
  90   }
  91 }
  92 
  93 + (void) eventCountPlusPlus{
  94   @synchronized(self) {
  95     eventCount++;
  96   }
  97 }
  98 
  99 + (jint) scrollStateWithEvent: (NSEvent*) event {
 100 
 101     if ([event type] != NSScrollWheel) {
 102         return 0;
 103     }
 104 
 105     if ([event phase]) {
 106         // process a phase of manual scrolling
 107         switch ([event phase]) {
 108             case NSEventPhaseBegan: return SCROLL_PHASE_BEGAN;
 109             case NSEventPhaseCancelled: return SCROLL_PHASE_ENDED;
 110             case NSEventPhaseEnded: return SCROLL_PHASE_ENDED;
 111             default: return SCROLL_PHASE_CONTINUED;
 112         }
 113     }
 114 
 115     if ([event momentumPhase]) {
 116         // process a phase of automatic scrolling
 117         switch ([event momentumPhase]) {
 118             case NSEventPhaseBegan: return SCROLL_PHASE_MOMENTUM_BEGAN;
 119             case NSEventPhaseCancelled: return SCROLL_PHASE_ENDED;
 120             case NSEventPhaseEnded: return SCROLL_PHASE_ENDED;
 121             default: return SCROLL_PHASE_CONTINUED;
 122         }
 123     }
 124     // phase and momentum phase both are not set
 125     return SCROLL_PHASE_UNSUPPORTED;
 126 }
 127 
 128 + (BOOL) hasPreciseScrollingDeltas: (NSEvent*) event {
 129     return [event type] == NSScrollWheel
 130         && [event respondsToSelector:@selector(hasPreciseScrollingDeltas)]
 131         && [event hasPreciseScrollingDeltas];
 132 }
 133 @end
 134 
 135 
 136 @interface AWTRunLoopObject : NSObject {
 137     BOOL _shouldEndRunLoop;
 138 }
 139 @end
 140 
 141 @implementation AWTRunLoopObject
 142 
 143 - (id) init {
 144     self = [super init];
 145     if (self != nil) {
 146         _shouldEndRunLoop = NO;
 147     }
 148     return self;
 149 }
 150 
 151 - (BOOL) shouldEndRunLoop {
 152     return _shouldEndRunLoop;
 153 }
 154 
 155 - (void) endRunLoop {
 156     _shouldEndRunLoop = YES;
 157 }
 158 
 159 @end
 160 
 161 @interface JavaRunnable : NSObject { }
 162 @property jobject runnable;
 163 - (id)initWithRunnable:(jobject)gRunnable;
 164 - (void)perform;
 165 @end
 166 
 167 @implementation JavaRunnable
 168 @synthesize runnable = _runnable;
 169 
 170 - (id)initWithRunnable:(jobject)gRunnable {
 171     if (self = [super init]) {
 172         self.runnable = gRunnable;
 173     }
 174     return self;
 175 }
 176 
 177 - (void)dealloc {
 178     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
 179     if (self.runnable) {
 180         (*env)->DeleteGlobalRef(env, self.runnable);
 181     }
 182     [super dealloc];
 183 }
 184 
 185 - (void)perform {
 186     JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
 187     static JNF_CLASS_CACHE(sjc_Runnable, "java/lang/Runnable");
 188     static JNF_MEMBER_CACHE(jm_Runnable_run, sjc_Runnable, "run", "()V");
 189     JNFCallVoidMethod(env, self.runnable, jm_Runnable_run);
 190     [self release];
 191 }
 192 @end
 193 
 194 void setBusy(BOOL busy) {
 195     AWT_ASSERT_APPKIT_THREAD;
 196 
 197     JNIEnv *env = [ThreadUtilities getJNIEnv];
 198     static JNF_CLASS_CACHE(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");
 199 
 200     if (busy) {
 201         static JNF_STATIC_MEMBER_CACHE(jm_notifyBusyMethod, jc_AWTAutoShutdown, "notifyToolkitThreadBusy", "()V");
 202         JNFCallStaticVoidMethod(env, jm_notifyBusyMethod);
 203     } else {
 204         static JNF_STATIC_MEMBER_CACHE(jm_notifyFreeMethod, jc_AWTAutoShutdown, "notifyToolkitThreadFree", "()V");
 205         JNFCallStaticVoidMethod(env, jm_notifyFreeMethod);
 206     }
 207 }
 208 
 209 static void setUpAWTAppKit(BOOL installObservers)
 210 {
 211     if (installObservers) {
 212         AWT_STARTUP_LOG(@"Setting up busy observers");
 213 
 214         // Add CFRunLoopObservers to call into AWT so that AWT knows that the
 215         //  AWT thread (which is the AppKit main thread) is alive. This way AWT
 216         //  will not automatically shutdown.
 217         CFRunLoopObserverRef busyObserver = CFRunLoopObserverCreateWithHandler(
 218                                                NULL,                        // CFAllocator
 219                                                kCFRunLoopAfterWaiting,      // CFOptionFlags
 220                                                true,                        // repeats
 221                                                NSIntegerMax,                // order
 222                                                ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
 223                                                    setBusy(YES);
 224                                                });
 225 
 226         CFRunLoopObserverRef notBusyObserver = CFRunLoopObserverCreateWithHandler(
 227                                                 NULL,                        // CFAllocator
 228                                                 kCFRunLoopBeforeWaiting,     // CFOptionFlags
 229                                                 true,                        // repeats
 230                                                 NSIntegerMin,                // order
 231                                                 ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
 232                                                     setBusy(NO);
 233                                                 });
 234 
 235         CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
 236         CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
 237         CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
 238 
 239         CFRelease(busyObserver);
 240         CFRelease(notBusyObserver);
 241 
 242         setBusy(YES);
 243     }
 244 
 245     JNIEnv* env = [ThreadUtilities getJNIEnv];
 246     static JNF_CLASS_CACHE(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
 247     static JNF_STATIC_MEMBER_CACHE(jsm_installToolkitThreadInJava, jc_LWCToolkit, "installToolkitThreadInJava", "()V");
 248     JNFCallStaticVoidMethod(env, jsm_installToolkitThreadInJava);
 249 }
 250 
 251 BOOL isSWTInWebStart(JNIEnv* env) {
 252     NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env];
 253     return [@"true" isCaseInsensitiveLike:swtWebStart];
 254 }
 255 
 256 static void AWT_NSUncaughtExceptionHandler(NSException *exception) {
 257     NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
 258 }
 259 
 260 @interface AWTStarter : NSObject
 261 + (void)start:(BOOL)headless;
 262 + (void)starter:(BOOL)onMainThread headless:(BOOL)headless;
 263 + (void)appKitIsRunning:(id)arg;
 264 @end
 265 
 266 @implementation AWTStarter
 267 
 268 + (BOOL) isConnectedToWindowServer {
 269     SecuritySessionId session_id;
 270     SessionAttributeBits session_info;
 271     OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
 272     if (status != noErr) return NO;
 273     if (!(session_info & sessionHasGraphicAccess)) return NO;
 274     return YES;
 275 }
 276 
 277 + (BOOL) markAppAsDaemon {
 278     id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");
 279     SEL markAppSel = @selector(markAppIsDaemon);
 280     if (![jrsAppKitAWTClass respondsToSelector:markAppSel]) return NO;
 281     return [jrsAppKitAWTClass performSelector:markAppSel] ? YES : NO;
 282 }
 283 
 284 + (void)appKitIsRunning:(id)arg {
 285     AWT_ASSERT_APPKIT_THREAD;
 286     AWT_STARTUP_LOG(@"About to message AppKit started");
 287 
 288     // Signal that AppKit has started (or is already running).
 289     pthread_mutex_lock(&sAppKitStarted_mutex);
 290     sAppKitStarted = YES;
 291     pthread_cond_signal(&sAppKitStarted_cv);
 292     pthread_mutex_unlock(&sAppKitStarted_mutex);
 293 
 294     AWT_STARTUP_LOG(@"Finished messaging AppKit started");
 295 }
 296 
 297 + (void)start:(BOOL)headless
 298 {
 299     // onMainThread is NOT the same at SWT mode!
 300     // If the JVM was started on the first thread for SWT, but the SWT loads the AWT on a secondary thread,
 301     // onMainThread here will be false but SWT mode will be true.  If we are currently on the main thread, we don't
 302     // need to throw AWT startup over to another thread.
 303     BOOL onMainThread = [NSThread isMainThread];
 304 
 305     NSString* msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread];
 306     AWT_STARTUP_LOG(msg);
 307 
 308     if (!headless)
 309     {
 310         // Listen for the NSApp to start. This indicates that JNI_OnLoad can proceed.
 311         //  It must wait because there is a chance that another java thread will grab
 312         //  the AppKit lock before the +[NSApplication sharedApplication] returns.
 313         //  See <rdar://problem/3492666> for an example.
 314         [[NSNotificationCenter defaultCenter] addObserver:[AWTStarter class]
 315                                                  selector:@selector(appKitIsRunning:)
 316                                                      name:NSApplicationDidFinishLaunchingNotification
 317                                                    object:nil];
 318 
 319         AWT_STARTUP_LOG(@"+[AWTStarter start:::]: registered NSApplicationDidFinishLaunchingNotification");
 320     }
 321 
 322     [ThreadUtilities performOnMainThreadWaiting:NO block:^() {
 323         [AWTStarter starter:onMainThread headless:headless];
 324     }];
 325 
 326 
 327     if (!headless && !onMainThread) {
 328 
 329         AWT_STARTUP_LOG(@"about to wait on AppKit startup mutex");
 330 
 331         // Wait here for AppKit to have started (or for AWT to have been loaded into
 332         //  an already running NSApplication).
 333         pthread_mutex_lock(&sAppKitStarted_mutex);
 334         while (sAppKitStarted == NO) {
 335             pthread_cond_wait(&sAppKitStarted_cv, &sAppKitStarted_mutex);
 336         }
 337         pthread_mutex_unlock(&sAppKitStarted_mutex);
 338 
 339         // AWT gets here AFTER +[AWTStarter appKitIsRunning:] is called.
 340         AWT_STARTUP_LOG(@"got out of the AppKit startup mutex");
 341     }
 342 
 343     if (!headless) {
 344         // Don't set the delegate until the NSApplication has been created and
 345         // its finishLaunching has initialized it.
 346         //  ApplicationDelegate is the support code for com.apple.eawt.
 347         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 348             id<NSApplicationDelegate> delegate = [ApplicationDelegate sharedDelegate];
 349             if (delegate != nil) {
 350                 OSXAPP_SetApplicationDelegate(delegate);
 351             }
 352         }];
 353     }
 354 }
 355 
 356 + (void)starter:(BOOL)wasOnMainThread headless:(BOOL)headless {
 357     NSAutoreleasePool *pool = [NSAutoreleasePool new];
 358     // Add the exception handler of last resort
 359     NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
 360 
 361     // Headless mode trumps either ordinary AWT or SWT-in-AWT mode.  Declare us a daemon and return.
 362     if (headless) {
 363         // Note that we don't install run loop observers in headless mode
 364         // because we don't need them (see 7174704)
 365         if (!forceEmbeddedMode) {
 366             setUpAWTAppKit(false);
 367         }
 368         [AWTStarter markAppAsDaemon];
 369         return;
 370     }
 371 
 372     if (forceEmbeddedMode) {
 373         AWT_STARTUP_LOG(@"in SWT or SWT/WebStart mode");
 374 
 375         // Init a default NSApplication instance instead of the NSApplicationAWT.
 376         // Note that [NSApp isRunning] will return YES after that, though
 377         // this behavior isn't specified anywhere. We rely on that.
 378         NSApplicationLoad();
 379     }
 380 
 381     // This will create a NSApplicationAWT for standalone AWT programs, unless there is
 382     //  already a NSApplication instance. If there is already a NSApplication instance,
 383     //  and -[NSApplication isRunning] returns YES, AWT is embedded inside another
 384     //  AppKit Application.
 385     NSApplication *app = [NSApplicationAWT sharedApplication];
 386     isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]];
 387 
 388     if (!isEmbedded) {
 389         // Install run loop observers and set the AppKit Java thread name
 390         setUpAWTAppKit(true);
 391     }
 392 
 393     // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
 394     if (![app isRunning]) {
 395         AWT_STARTUP_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
 396         // This is where the AWT AppKit thread parks itself to process events.
 397         [NSApplicationAWT runAWTLoopWithApp: app];
 398     } else {
 399         // We're either embedded, or showing a splash screen
 400         if (isEmbedded) {
 401             AWT_STARTUP_LOG(@"running embedded");
 402 
 403             // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
 404             setBusy(NO);
 405         } else {
 406             AWT_STARTUP_LOG(@"running after showing a splash screen");
 407         }
 408 
 409         // Signal so that JNI_OnLoad can proceed.
 410         if (!wasOnMainThread) [AWTStarter appKitIsRunning:nil];
 411 
 412         // Proceed to exit this call as there is no reason to run the NSApplication event loop.
 413     }
 414 
 415     [pool drain];
 416 }
 417 
 418 @end
 419 
 420 /*
 421  * Class:     sun_lwawt_macosx_LWCToolkit
 422  * Method:    nativeSyncQueue
 423  * Signature: (J)Z
 424  */
 425 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_nativeSyncQueue
 426 (JNIEnv *env, jobject self, jlong timeout)
 427 {
 428     long currentEventNum = [AWTToolkit getEventCount];
 429 
 430     NSApplication* sharedApp = [NSApplication sharedApplication];
 431     if ([sharedApp isKindOfClass:[NSApplicationAWT class]]) {
 432         NSApplicationAWT* theApp = (NSApplicationAWT*)sharedApp;
 433         // We use two different API to post events to the application,
 434         //  - [NSApplication postEvent]
 435         //  - CGEventPost(), see CRobot.m
 436         // It was found that if we post an event via CGEventPost in robot and
 437         // immediately after this we will post the second event via
 438         // [NSApp postEvent] then sometimes the second event will be handled
 439         // first. The opposite isn't proved, but we use both here to be safer.
 440 
 441         // If the native drag is in progress, skip native sync.
 442         if (!AWTToolkit.inDoDragDropLoop) {
 443             [theApp postDummyEvent:false];
 444             [theApp waitForDummyEvent:timeout / 2.0];
 445         }
 446         if (!AWTToolkit.inDoDragDropLoop) {
 447             [theApp postDummyEvent:true];
 448             [theApp waitForDummyEvent:timeout / 2.0];
 449         }
 450 
 451     } else {
 452         // could happen if we are embedded inside SWT application,
 453         // in this case just spin a single empty block through
 454         // the event loop to give it a chance to process pending events
 455         [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){}];
 456     }
 457 
 458     if (([AWTToolkit getEventCount] - currentEventNum) != 0) {
 459         return JNI_TRUE;
 460     }
 461 
 462     return JNI_FALSE;
 463 }
 464 
 465 /*
 466  * Class:     sun_lwawt_macosx_LWCToolkit
 467  * Method:    flushNativeSelectors
 468  * Signature: ()J
 469  */
 470 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_flushNativeSelectors
 471 (JNIEnv *env, jclass clz)
 472 {
 473 JNF_COCOA_ENTER(env);
 474         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){}];
 475 JNF_COCOA_EXIT(env);
 476 }
 477 
 478 /*
 479  * Class:     sun_lwawt_macosx_LWCToolkit
 480  * Method:    beep
 481  * Signature: ()V
 482  */
 483 JNIEXPORT void JNICALL
 484 Java_sun_lwawt_macosx_LWCToolkit_beep
 485 (JNIEnv *env, jobject self)
 486 {
 487     NSBeep(); // produces both sound and visual flash, if configured in System Preferences
 488 }
 489 
 490 static UInt32 RGB(NSColor *c) {
 491     c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
 492     if (c == nil)
 493     {
 494         return -1; // opaque white
 495     }
 496 
 497     CGFloat r, g, b, a;
 498     [c getRed:&r green:&g blue:&b alpha:&a];
 499 
 500     UInt32 ir = (UInt32) (r*255+0.5),
 501     ig = (UInt32) (g*255+0.5),
 502     ib = (UInt32) (b*255+0.5),
 503     ia = (UInt32) (a*255+0.5);
 504 
 505     //    NSLog(@"%@ %d, %d, %d", c, ir, ig, ib);
 506 
 507     return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0);
 508 }
 509 
 510 BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
 511     jint len = (*env)->GetArrayLength(env, jColors);
 512 
 513     UInt32 colorsArray[len];
 514     UInt32 *colors = colorsArray;
 515 
 516     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 517         NSUInteger i;
 518         for (i = 0; i < len; i++) {
 519             colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]);
 520         }
 521     }];
 522 
 523     jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0);
 524     if (_colors == NULL) {
 525         return NO;
 526     }
 527     memcpy(_colors, colors, len * sizeof(UInt32));
 528     (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0);
 529     return YES;
 530 }
 531 
 532 /**
 533  * Class:     sun_lwawt_macosx_LWCToolkit
 534  * Method:    loadNativeColors
 535  * Signature: ([I[I)V
 536  */
 537 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_loadNativeColors
 538 (JNIEnv *env, jobject peer, jintArray jSystemColors, jintArray jAppleColors)
 539 {
 540 JNF_COCOA_ENTER(env);
 541     if (doLoadNativeColors(env, jSystemColors, NO)) {
 542         doLoadNativeColors(env, jAppleColors, YES);
 543     }
 544 JNF_COCOA_EXIT(env);
 545 }
 546 
 547 /*
 548  * Class:     sun_lwawt_macosx_LWCToolkit
 549  * Method:    createAWTRunLoopMediator
 550  * Signature: ()J
 551  */
 552 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_LWCToolkit_createAWTRunLoopMediator
 553 (JNIEnv *env, jclass clz)
 554 {
 555 AWT_ASSERT_APPKIT_THREAD;
 556 
 557     jlong result;
 558 
 559 JNF_COCOA_ENTER(env);
 560     // We double retain because this object is owned by both main thread and "other" thread
 561     // We release in both doAWTRunLoop and stopAWTRunLoop
 562     result = ptr_to_jlong([[[AWTRunLoopObject alloc] init] retain]);
 563 JNF_COCOA_EXIT(env);
 564 
 565     return result;
 566 }
 567 
 568 /*
 569  * Class:     sun_lwawt_macosx_LWCToolkit
 570  * Method:    doAWTRunLoopImpl
 571  * Signature: (JZZ)V
 572  */
 573 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoopImpl
 574 (JNIEnv *env, jclass clz, jlong mediator, jboolean processEvents, jboolean inAWT)
 575 {
 576 AWT_ASSERT_APPKIT_THREAD;
 577 JNF_COCOA_ENTER(env);
 578 
 579     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 580 
 581     if (mediatorObject == nil) return;
 582 
 583     // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 584     BOOL isRunning = true;
 585     while (![mediatorObject shouldEndRunLoop] && isRunning) {
 586         isRunning = [[NSRunLoop currentRunLoop] runMode:(inAWT ? [JNFRunLoop javaRunLoopMode] : NSDefaultRunLoopMode)
 587                                              beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]];
 588         if (processEvents) {
 589             //We do not spin a runloop here as date is nil, so does not matter which mode to use
 590             // Processing all events excluding NSApplicationDefined which need to be processed
 591             // on the main loop only (those events are intended for disposing resources)
 592             NSEvent *event;
 593             if ((event = [NSApp nextEventMatchingMask:(NSAnyEventMask & ~NSApplicationDefinedMask)
 594                                            untilDate:nil
 595                                               inMode:NSDefaultRunLoopMode
 596                                              dequeue:YES]) != nil) {
 597                 [NSApp sendEvent:event];
 598             }
 599 
 600         }
 601     }
 602     [mediatorObject release];
 603 JNF_COCOA_EXIT(env);
 604 }
 605 
 606 /*
 607  * Class:     sun_lwawt_macosx_LWCToolkit
 608  * Method:    stopAWTRunLoop
 609  * Signature: (J)V
 610  */
 611 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop
 612 (JNIEnv *env, jclass clz, jlong mediator)
 613 {
 614 JNF_COCOA_ENTER(env);
 615 
 616     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 617 
 618     [ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
 619 
 620     [mediatorObject release];
 621 
 622 JNF_COCOA_EXIT(env);
 623 }
 624 
 625 /*
 626  * Class:     sun_lwawt_macosx_LWCToolkit
 627  * Method:    performOnMainThreadAfterDelay
 628  * Signature: (Ljava/lang/Runnable;J)V
 629  */
 630 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAfterDelay
 631 (JNIEnv *env, jclass clz, jobject runnable, jlong delay)
 632 {
 633 JNF_COCOA_ENTER(env);
 634     jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
 635     CHECK_NULL(gRunnable);
 636     [ThreadUtilities performOnMainThreadWaiting:NO block:^() {
 637         JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
 638         [performer performSelector:@selector(perform) withObject:nil afterDelay:(delay/1000.0)];
 639     }];
 640 JNF_COCOA_EXIT(env);
 641 }
 642 
 643 
 644 /*
 645  * Class:     sun_lwawt_macosx_LWCToolkit
 646  * Method:    isCapsLockOn
 647  * Signature: ()Z
 648  */
 649 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 650 (JNIEnv *env, jobject self)
 651 {
 652     __block jboolean isOn = JNI_FALSE;
 653     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 654         NSUInteger modifiers = [NSEvent modifierFlags];
 655         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 656     }];
 657 
 658     return isOn;
 659 }
 660 
 661 /*
 662  * Class:     sun_lwawt_macosx_LWCToolkit
 663  * Method:    isApplicationActive
 664  * Signature: ()Z
 665  */
 666 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 667 (JNIEnv *env, jclass clazz)
 668 {
 669     __block jboolean active = JNI_FALSE;
 670 
 671 JNF_COCOA_ENTER(env);
 672 
 673     [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
 674         active = (jboolean)[NSRunningApplication currentApplication].active;
 675     }];
 676 
 677 JNF_COCOA_EXIT(env);
 678 
 679     return active;
 680 }
 681 
 682 /*
 683  * Class:     sun_lwawt_macosx_LWCToolkit
 684  * Method:    activateApplicationIgnoringOtherApps
 685  * Signature: ()V
 686  */
 687 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_activateApplicationIgnoringOtherApps
 688 (JNIEnv *env, jclass clazz)
 689 {
 690     JNF_COCOA_ENTER(env);
 691     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 692         if(![NSApp isActive]){
 693             [NSApp activateIgnoringOtherApps:YES];
 694         }
 695     }];
 696     JNF_COCOA_EXIT(env);
 697 }
 698 
 699 
 700 /*
 701  * Class:     sun_awt_SunToolkit
 702  * Method:    closeSplashScreen
 703  * Signature: ()V
 704  */
 705 JNIEXPORT void JNICALL
 706 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 707 {
 708     void *hSplashLib = dlopen(0, RTLD_LAZY);
 709     if (!hSplashLib) return;
 710 
 711     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 712     if (splashClose) {
 713         splashClose();
 714     }
 715     dlclose(hSplashLib);
 716 }
 717 
 718 
 719 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 720 // solaris tree)...
 721 
 722 JNIEXPORT jstring JNICALL
 723 Java_sun_font_FontManager_getFontPath
 724 (JNIEnv *env, jclass obj, jboolean noType1)
 725 {
 726     return JNFNSToJavaString(env, @"/Library/Fonts");
 727 }
 728 
 729 // This isn't yet used on unix, the implementation is added since shared
 730 // code calls this method in preparation for future use.
 731 JNIEXPORT void JNICALL
 732 Java_sun_font_FontManager_populateFontFileNameMap
 733 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 734 {
 735 
 736 }
 737 
 738 /*
 739  * Class:     sun_lwawt_macosx_LWCToolkit
 740  * Method:    initIDs
 741  * Signature: ()V
 742  */
 743 JNIEXPORT void JNICALL
 744 Java_sun_lwawt_macosx_LWCToolkit_initIDs
 745 (JNIEnv *env, jclass klass) {
 746 
 747     JNF_COCOA_ENTER(env)
 748 
 749     gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS;
 750 
 751     jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
 752     CHECK_NULL(inputEventClazz);
 753     jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
 754     CHECK_NULL(getButtonDownMasksID);
 755     jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
 756     jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
 757     CHECK_NULL(tmp);
 758 
 759     gButtonDownMasks = (jint*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), gNumberOfButtons);
 760     if (gButtonDownMasks == NULL) {
 761         gNumberOfButtons = 0;
 762         (*env)->ReleaseIntArrayElements(env, obj, tmp, JNI_ABORT);
 763         JNU_ThrowOutOfMemoryError(env, NULL);
 764         return;
 765     }
 766 
 767     int i;
 768     for (i = 0; i < gNumberOfButtons; i++) {
 769         gButtonDownMasks[i] = tmp[i];
 770     }
 771 
 772     (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
 773     (*env)->DeleteLocalRef(env, obj);
 774 
 775     JNF_COCOA_EXIT(env)
 776 }
 777 
 778 /*
 779  * Class:     sun_lwawt_macosx_LWCToolkit
 780  * Method:    initAppkit
 781  * Signature: (Ljava/lang/ThreadGroup;)V
 782  */
 783 JNIEXPORT void JNICALL
 784 Java_sun_lwawt_macosx_LWCToolkit_initAppkit
 785 (JNIEnv *env, jclass klass, jobject appkitThreadGroup, jboolean headless) {
 786     JNF_COCOA_ENTER(env)
 787 
 788     [ThreadUtilities setAppkitThreadGroup:(*env)->NewGlobalRef(env, appkitThreadGroup)];
 789 
 790     // Launcher sets this env variable if -XstartOnFirstThread is specified
 791     char envVar[80];
 792     snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
 793     if (getenv(envVar) != NULL) {
 794         forceEmbeddedMode = YES;
 795         unsetenv(envVar);
 796     }
 797 
 798     if (isSWTInWebStart(env)) {
 799         forceEmbeddedMode = YES;
 800     }
 801 
 802     [AWTStarter start:headless ? YES : NO];
 803 
 804     JNF_COCOA_EXIT(env)
 805 }
 806 
 807 JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
 808     OSXAPP_SetJavaVM(vm);
 809 
 810     // We need to let Foundation know that this is a multithreaded application,
 811     // if it isn't already.
 812     if (![NSThread isMultiThreaded]) {
 813         [[[[NSThread alloc] init] autorelease] start];
 814     }
 815 
 816     return JNI_VERSION_1_4;
 817 }
 818 
 819 /*
 820  * Class:     sun_lwawt_macosx_LWCToolkit
 821  * Method:    isEmbedded
 822  * Signature: ()Z
 823  */
 824 JNIEXPORT jboolean JNICALL
 825 Java_sun_lwawt_macosx_LWCToolkit_isEmbedded
 826 (JNIEnv *env, jclass klass) {
 827     return isEmbedded ? JNI_TRUE : JNI_FALSE;
 828 }
 829 
 830 /*
 831  * Class:     sun_lwawt_macosx_LWCToolkit
 832  * Method:    isInAquaSession
 833  * Signature: ()Z
 834  */
 835 JNIEXPORT jboolean JNICALL
 836 Java_sun_lwawt_macosx_LWCToolkit_isInAquaSession
 837 (JNIEnv *env, jclass klass) {
 838     // copied from java.base/macosx/native/libjava/java_props_macosx.c
 839     // environment variable to bypass the aqua session check
 840     char *ev = getenv("AWT_FORCE_HEADFUL");
 841     if (ev && (strncasecmp(ev, "true", 4) == 0)) {
 842         // if "true" then tell the caller we're in an Aqua session without
 843         // actually checking
 844         return JNI_TRUE;
 845     }
 846     // Is the WindowServer available?
 847     SecuritySessionId session_id;
 848     SessionAttributeBits session_info;
 849     OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
 850     if (status == noErr) {
 851         if (session_info & sessionHasGraphicAccess) {
 852             return JNI_TRUE;
 853         }
 854     }
 855     return JNI_FALSE;
 856 }