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 #import "JNIUtilities.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     DECLARE_CLASS(sjc_Runnable, "java/lang/Runnable");
 188     DECLARE_METHOD(jm_Runnable_run, sjc_Runnable, "run", "()V");
 189     (*env)->CallVoidMethod(env, self.runnable, jm_Runnable_run);
 190     CHECK_EXCEPTION();
 191     [self release];
 192 }
 193 @end
 194 
 195 void setBusy(BOOL busy) {
 196     AWT_ASSERT_APPKIT_THREAD;
 197 
 198     JNIEnv *env = [ThreadUtilities getJNIEnv];
 199     DECLARE_CLASS(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");
 200 
 201     if (busy) {
 202         DECLARE_STATIC_METHOD(jm_notifyBusyMethod, jc_AWTAutoShutdown, "notifyToolkitThreadBusy", "()V");
 203         (*env)->CallStaticVoidMethod(env, jc_AWTAutoShutdown, jm_notifyBusyMethod);
 204     } else {
 205         DECLARE_STATIC_METHOD(jm_notifyFreeMethod, jc_AWTAutoShutdown, "notifyToolkitThreadFree", "()V");
 206         (*env)->CallStaticVoidMethod(env, jc_AWTAutoShutdown, jm_notifyFreeMethod);
 207     }
 208      CHECK_EXCEPTION();
 209 }
 210 
 211 static void setUpAWTAppKit(BOOL installObservers)
 212 {
 213     if (installObservers) {
 214         AWT_STARTUP_LOG(@"Setting up busy observers");
 215 
 216         // Add CFRunLoopObservers to call into AWT so that AWT knows that the
 217         //  AWT thread (which is the AppKit main thread) is alive. This way AWT
 218         //  will not automatically shutdown.
 219         CFRunLoopObserverRef busyObserver = CFRunLoopObserverCreateWithHandler(
 220                                                NULL,                        // CFAllocator
 221                                                kCFRunLoopAfterWaiting,      // CFOptionFlags
 222                                                true,                        // repeats
 223                                                NSIntegerMax,                // order
 224                                                ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
 225                                                    setBusy(YES);
 226                                                });
 227 
 228         CFRunLoopObserverRef notBusyObserver = CFRunLoopObserverCreateWithHandler(
 229                                                 NULL,                        // CFAllocator
 230                                                 kCFRunLoopBeforeWaiting,     // CFOptionFlags
 231                                                 true,                        // repeats
 232                                                 NSIntegerMin,                // order
 233                                                 ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
 234                                                     setBusy(NO);
 235                                                 });
 236 
 237         CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
 238         CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
 239         CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
 240 
 241         CFRelease(busyObserver);
 242         CFRelease(notBusyObserver);
 243 
 244         setBusy(YES);
 245     }
 246 
 247     JNIEnv* env = [ThreadUtilities getJNIEnv];
 248     DECLARE_CLASS(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
 249     DECLARE_STATIC_METHOD(jsm_installToolkitThreadInJava, jc_LWCToolkit, "installToolkitThreadInJava", "()V");
 250     (*env)->CallStaticVoidMethod(env, jc_LWCToolkit, jsm_installToolkitThreadInJava);
 251     CHECK_EXCEPTION();
 252 
 253 }
 254 
 255 BOOL isSWTInWebStart(JNIEnv* env) {
 256     NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env];
 257     return [@"true" isCaseInsensitiveLike:swtWebStart];
 258 }
 259 
 260 static void AWT_NSUncaughtExceptionHandler(NSException *exception) {
 261     NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
 262 }
 263 
 264 @interface AWTStarter : NSObject
 265 + (void)start:(BOOL)headless;
 266 + (void)starter:(BOOL)onMainThread headless:(BOOL)headless;
 267 + (void)appKitIsRunning:(id)arg;
 268 @end
 269 
 270 @implementation AWTStarter
 271 
 272 + (BOOL) isConnectedToWindowServer {
 273     SecuritySessionId session_id;
 274     SessionAttributeBits session_info;
 275     OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
 276     if (status != noErr) return NO;
 277     if (!(session_info & sessionHasGraphicAccess)) return NO;
 278     return YES;
 279 }
 280 
 281 + (BOOL) markAppAsDaemon {
 282     id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");
 283     SEL markAppSel = @selector(markAppIsDaemon);
 284     if (![jrsAppKitAWTClass respondsToSelector:markAppSel]) return NO;
 285     return [jrsAppKitAWTClass performSelector:markAppSel] ? YES : NO;
 286 }
 287 
 288 + (void)appKitIsRunning:(id)arg {
 289     AWT_ASSERT_APPKIT_THREAD;
 290     AWT_STARTUP_LOG(@"About to message AppKit started");
 291 
 292     // Signal that AppKit has started (or is already running).
 293     pthread_mutex_lock(&sAppKitStarted_mutex);
 294     sAppKitStarted = YES;
 295     pthread_cond_signal(&sAppKitStarted_cv);
 296     pthread_mutex_unlock(&sAppKitStarted_mutex);
 297 
 298     AWT_STARTUP_LOG(@"Finished messaging AppKit started");
 299 }
 300 
 301 + (void)start:(BOOL)headless
 302 {
 303     // onMainThread is NOT the same at SWT mode!
 304     // If the JVM was started on the first thread for SWT, but the SWT loads the AWT on a secondary thread,
 305     // onMainThread here will be false but SWT mode will be true.  If we are currently on the main thread, we don't
 306     // need to throw AWT startup over to another thread.
 307     BOOL onMainThread = [NSThread isMainThread];
 308 
 309     NSString* msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread];
 310     AWT_STARTUP_LOG(msg);
 311 
 312     if (!headless)
 313     {
 314         // Listen for the NSApp to start. This indicates that JNI_OnLoad can proceed.
 315         //  It must wait because there is a chance that another java thread will grab
 316         //  the AppKit lock before the +[NSApplication sharedApplication] returns.
 317         //  See <rdar://problem/3492666> for an example.
 318         [[NSNotificationCenter defaultCenter] addObserver:[AWTStarter class]
 319                                                  selector:@selector(appKitIsRunning:)
 320                                                      name:NSApplicationDidFinishLaunchingNotification
 321                                                    object:nil];
 322 
 323         AWT_STARTUP_LOG(@"+[AWTStarter start:::]: registered NSApplicationDidFinishLaunchingNotification");
 324     }
 325 
 326     [ThreadUtilities performOnMainThreadWaiting:NO block:^() {
 327         [AWTStarter starter:onMainThread headless:headless];
 328     }];
 329 
 330 
 331     if (!headless && !onMainThread) {
 332 
 333         AWT_STARTUP_LOG(@"about to wait on AppKit startup mutex");
 334 
 335         // Wait here for AppKit to have started (or for AWT to have been loaded into
 336         //  an already running NSApplication).
 337         pthread_mutex_lock(&sAppKitStarted_mutex);
 338         while (sAppKitStarted == NO) {
 339             pthread_cond_wait(&sAppKitStarted_cv, &sAppKitStarted_mutex);
 340         }
 341         pthread_mutex_unlock(&sAppKitStarted_mutex);
 342 
 343         // AWT gets here AFTER +[AWTStarter appKitIsRunning:] is called.
 344         AWT_STARTUP_LOG(@"got out of the AppKit startup mutex");
 345     }
 346 
 347     if (!headless) {
 348         // Don't set the delegate until the NSApplication has been created and
 349         // its finishLaunching has initialized it.
 350         //  ApplicationDelegate is the support code for com.apple.eawt.
 351         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 352             id<NSApplicationDelegate> delegate = [ApplicationDelegate sharedDelegate];
 353             if (delegate != nil) {
 354                 OSXAPP_SetApplicationDelegate(delegate);
 355             }
 356         }];
 357     }
 358 }
 359 
 360 + (void)starter:(BOOL)wasOnMainThread headless:(BOOL)headless {
 361     NSAutoreleasePool *pool = [NSAutoreleasePool new];
 362     // Add the exception handler of last resort
 363     NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
 364 
 365     // Headless mode trumps either ordinary AWT or SWT-in-AWT mode.  Declare us a daemon and return.
 366     if (headless) {
 367         // Note that we don't install run loop observers in headless mode
 368         // because we don't need them (see 7174704)
 369         if (!forceEmbeddedMode) {
 370             setUpAWTAppKit(false);
 371         }
 372         [AWTStarter markAppAsDaemon];
 373         return;
 374     }
 375 
 376     if (forceEmbeddedMode) {
 377         AWT_STARTUP_LOG(@"in SWT or SWT/WebStart mode");
 378 
 379         // Init a default NSApplication instance instead of the NSApplicationAWT.
 380         // Note that [NSApp isRunning] will return YES after that, though
 381         // this behavior isn't specified anywhere. We rely on that.
 382         NSApplicationLoad();
 383     }
 384 
 385     // This will create a NSApplicationAWT for standalone AWT programs, unless there is
 386     //  already a NSApplication instance. If there is already a NSApplication instance,
 387     //  and -[NSApplication isRunning] returns YES, AWT is embedded inside another
 388     //  AppKit Application.
 389     NSApplication *app = [NSApplicationAWT sharedApplication];
 390     isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]];
 391 
 392     if (!isEmbedded) {
 393         // Install run loop observers and set the AppKit Java thread name
 394         setUpAWTAppKit(true);
 395     }
 396 
 397     // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
 398     if (![app isRunning]) {
 399         AWT_STARTUP_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
 400         // This is where the AWT AppKit thread parks itself to process events.
 401         [NSApplicationAWT runAWTLoopWithApp: app];
 402     } else {
 403         // We're either embedded, or showing a splash screen
 404         if (isEmbedded) {
 405             AWT_STARTUP_LOG(@"running embedded");
 406 
 407             // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
 408             setBusy(NO);
 409         } else {
 410             AWT_STARTUP_LOG(@"running after showing a splash screen");
 411         }
 412 
 413         // Signal so that JNI_OnLoad can proceed.
 414         if (!wasOnMainThread) [AWTStarter appKitIsRunning:nil];
 415 
 416         // Proceed to exit this call as there is no reason to run the NSApplication event loop.
 417     }
 418 
 419     [pool drain];
 420 }
 421 
 422 @end
 423 
 424 /*
 425  * Class:     sun_lwawt_macosx_LWCToolkit
 426  * Method:    nativeSyncQueue
 427  * Signature: (J)Z
 428  */
 429 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_nativeSyncQueue
 430 (JNIEnv *env, jobject self, jlong timeout)
 431 {
 432     long currentEventNum = [AWTToolkit getEventCount];
 433 
 434     NSApplication* sharedApp = [NSApplication sharedApplication];
 435     if ([sharedApp isKindOfClass:[NSApplicationAWT class]]) {
 436         NSApplicationAWT* theApp = (NSApplicationAWT*)sharedApp;
 437         // We use two different API to post events to the application,
 438         //  - [NSApplication postEvent]
 439         //  - CGEventPost(), see CRobot.m
 440         // It was found that if we post an event via CGEventPost in robot and
 441         // immediately after this we will post the second event via
 442         // [NSApp postEvent] then sometimes the second event will be handled
 443         // first. The opposite isn't proved, but we use both here to be safer.
 444 
 445         // If the native drag is in progress, skip native sync.
 446         if (!AWTToolkit.inDoDragDropLoop) {
 447             [theApp postDummyEvent:false];
 448             [theApp waitForDummyEvent:timeout / 2.0];
 449         }
 450         if (!AWTToolkit.inDoDragDropLoop) {
 451             [theApp postDummyEvent:true];
 452             [theApp waitForDummyEvent:timeout / 2.0];
 453         }
 454 
 455     } else {
 456         // could happen if we are embedded inside SWT application,
 457         // in this case just spin a single empty block through
 458         // the event loop to give it a chance to process pending events
 459         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){}];
 460     }
 461 
 462     if (([AWTToolkit getEventCount] - currentEventNum) != 0) {
 463         return JNI_TRUE;
 464     }
 465 
 466     return JNI_FALSE;
 467 }
 468 
 469 /*
 470  * Class:     sun_lwawt_macosx_LWCToolkit
 471  * Method:    flushNativeSelectors
 472  * Signature: ()J
 473  */
 474 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_flushNativeSelectors
 475 (JNIEnv *env, jclass clz)
 476 {
 477 JNI_COCOA_ENTER(env);
 478         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){}];
 479 JNI_COCOA_EXIT(env);
 480 }
 481 
 482 /*
 483  * Class:     sun_lwawt_macosx_LWCToolkit
 484  * Method:    beep
 485  * Signature: ()V
 486  */
 487 JNIEXPORT void JNICALL
 488 Java_sun_lwawt_macosx_LWCToolkit_beep
 489 (JNIEnv *env, jobject self)
 490 {
 491     NSBeep(); // produces both sound and visual flash, if configured in System Preferences
 492 }
 493 
 494 static UInt32 RGB(NSColor *c) {
 495     c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
 496     if (c == nil)
 497     {
 498         return -1; // opaque white
 499     }
 500 
 501     CGFloat r, g, b, a;
 502     [c getRed:&r green:&g blue:&b alpha:&a];
 503 
 504     UInt32 ir = (UInt32) (r*255+0.5),
 505     ig = (UInt32) (g*255+0.5),
 506     ib = (UInt32) (b*255+0.5),
 507     ia = (UInt32) (a*255+0.5);
 508 
 509     //    NSLog(@"%@ %d, %d, %d", c, ir, ig, ib);
 510 
 511     return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0);
 512 }
 513 
 514 BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
 515     jint len = (*env)->GetArrayLength(env, jColors);
 516 
 517     UInt32 colorsArray[len];
 518     UInt32 *colors = colorsArray;
 519 
 520     [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 521         NSUInteger i;
 522         for (i = 0; i < len; i++) {
 523             colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]);
 524         }
 525     }];
 526 
 527     jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0);
 528     if (_colors == NULL) {
 529         return NO;
 530     }
 531     memcpy(_colors, colors, len * sizeof(UInt32));
 532     (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0);
 533     return YES;
 534 }
 535 
 536 /**
 537  * Class:     sun_lwawt_macosx_LWCToolkit
 538  * Method:    loadNativeColors
 539  * Signature: ([I[I)V
 540  */
 541 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_loadNativeColors
 542 (JNIEnv *env, jobject peer, jintArray jSystemColors, jintArray jAppleColors)
 543 {
 544 JNI_COCOA_ENTER(env);
 545     if (doLoadNativeColors(env, jSystemColors, NO)) {
 546         doLoadNativeColors(env, jAppleColors, YES);
 547     }
 548 JNI_COCOA_EXIT(env);
 549 }
 550 
 551 /*
 552  * Class:     sun_lwawt_macosx_LWCToolkit
 553  * Method:    createAWTRunLoopMediator
 554  * Signature: ()J
 555  */
 556 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_LWCToolkit_createAWTRunLoopMediator
 557 (JNIEnv *env, jclass clz)
 558 {
 559 AWT_ASSERT_APPKIT_THREAD;
 560 
 561     jlong result;
 562 
 563 JNI_COCOA_ENTER(env);
 564     // We double retain because this object is owned by both main thread and "other" thread
 565     // We release in both doAWTRunLoop and stopAWTRunLoop
 566     result = ptr_to_jlong([[[AWTRunLoopObject alloc] init] retain]);
 567 JNI_COCOA_EXIT(env);
 568 
 569     return result;
 570 }
 571 
 572 /*
 573  * Class:     sun_lwawt_macosx_LWCToolkit
 574  * Method:    doAWTRunLoopImpl
 575  * Signature: (JZZ)V
 576  */
 577 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoopImpl
 578 (JNIEnv *env, jclass clz, jlong mediator, jboolean processEvents, jboolean inAWT)
 579 {
 580 AWT_ASSERT_APPKIT_THREAD;
 581 JNI_COCOA_ENTER(env);
 582 
 583     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 584 
 585     if (mediatorObject == nil) return;
 586 
 587     // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 588     BOOL isRunning = true;
 589     while (![mediatorObject shouldEndRunLoop] && isRunning) {
 590         isRunning = [[NSRunLoop currentRunLoop] runMode:(inAWT ? [ThreadUtilities javaRunLoopMode] : NSDefaultRunLoopMode)
 591                                              beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]];
 592         if (processEvents) {
 593             //We do not spin a runloop here as date is nil, so does not matter which mode to use
 594             // Processing all events excluding NSApplicationDefined which need to be processed
 595             // on the main loop only (those events are intended for disposing resources)
 596             NSEvent *event;
 597             if ((event = [NSApp nextEventMatchingMask:(NSAnyEventMask & ~NSApplicationDefinedMask)
 598                                            untilDate:nil
 599                                               inMode:NSDefaultRunLoopMode
 600                                              dequeue:YES]) != nil) {
 601                 [NSApp sendEvent:event];
 602             }
 603 
 604         }
 605     }
 606     [mediatorObject release];
 607 JNI_COCOA_EXIT(env);
 608 }
 609 
 610 /*
 611  * Class:     sun_lwawt_macosx_LWCToolkit
 612  * Method:    stopAWTRunLoop
 613  * Signature: (J)V
 614  */
 615 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop
 616 (JNIEnv *env, jclass clz, jlong mediator)
 617 {
 618 JNI_COCOA_ENTER(env);
 619 
 620     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 621 
 622     [ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
 623 
 624     [mediatorObject release];
 625 
 626 JNI_COCOA_EXIT(env);
 627 }
 628 
 629 /*
 630  * Class:     sun_lwawt_macosx_LWCToolkit
 631  * Method:    performOnMainThreadAfterDelay
 632  * Signature: (Ljava/lang/Runnable;J)V
 633  */
 634 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAfterDelay
 635 (JNIEnv *env, jclass clz, jobject runnable, jlong delay)
 636 {
 637 JNI_COCOA_ENTER(env);
 638     jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
 639     CHECK_NULL(gRunnable);
 640     [ThreadUtilities performOnMainThreadWaiting:NO block:^() {
 641         JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
 642         [performer performSelector:@selector(perform) withObject:nil afterDelay:(delay/1000.0)];
 643     }];
 644 JNI_COCOA_EXIT(env);
 645 }
 646 
 647 
 648 /*
 649  * Class:     sun_lwawt_macosx_LWCToolkit
 650  * Method:    isCapsLockOn
 651  * Signature: ()Z
 652  */
 653 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 654 (JNIEnv *env, jobject self)
 655 {
 656     __block jboolean isOn = JNI_FALSE;
 657     [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 658         NSUInteger modifiers = [NSEvent modifierFlags];
 659         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 660     }];
 661 
 662     return isOn;
 663 }
 664 
 665 /*
 666  * Class:     sun_lwawt_macosx_LWCToolkit
 667  * Method:    isApplicationActive
 668  * Signature: ()Z
 669  */
 670 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 671 (JNIEnv *env, jclass clazz)
 672 {
 673     __block jboolean active = JNI_FALSE;
 674 
 675 JNI_COCOA_ENTER(env);
 676 
 677     [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
 678         active = (jboolean)[NSRunningApplication currentApplication].active;
 679     }];
 680 
 681 JNI_COCOA_EXIT(env);
 682 
 683     return active;
 684 }
 685 
 686 /*
 687  * Class:     sun_lwawt_macosx_LWCToolkit
 688  * Method:    activateApplicationIgnoringOtherApps
 689  * Signature: ()V
 690  */
 691 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_activateApplicationIgnoringOtherApps
 692 (JNIEnv *env, jclass clazz)
 693 {
 694     JNI_COCOA_ENTER(env);
 695     [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
 696         if(![NSApp isActive]){
 697             [NSApp activateIgnoringOtherApps:YES];
 698         }
 699     }];
 700     JNI_COCOA_EXIT(env);
 701 }
 702 
 703 
 704 /*
 705  * Class:     sun_awt_SunToolkit
 706  * Method:    closeSplashScreen
 707  * Signature: ()V
 708  */
 709 JNIEXPORT void JNICALL
 710 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 711 {
 712     void *hSplashLib = dlopen(0, RTLD_LAZY);
 713     if (!hSplashLib) return;
 714 
 715     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 716     if (splashClose) {
 717         splashClose();
 718     }
 719     dlclose(hSplashLib);
 720 }
 721 
 722 
 723 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 724 // solaris tree)...
 725 
 726 JNIEXPORT jstring JNICALL
 727 Java_sun_font_FontManager_getFontPath
 728 (JNIEnv *env, jclass obj, jboolean noType1)
 729 {
 730     return NSStringToJavaString(env, @"/Library/Fonts");
 731 }
 732 
 733 // This isn't yet used on unix, the implementation is added since shared
 734 // code calls this method in preparation for future use.
 735 JNIEXPORT void JNICALL
 736 Java_sun_font_FontManager_populateFontFileNameMap
 737 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 738 {
 739 
 740 }
 741 
 742 /*
 743  * Class:     sun_lwawt_macosx_LWCToolkit
 744  * Method:    initIDs
 745  * Signature: ()V
 746  */
 747 JNIEXPORT void JNICALL
 748 Java_sun_lwawt_macosx_LWCToolkit_initIDs
 749 (JNIEnv *env, jclass klass) {
 750 
 751     JNI_COCOA_ENTER(env);
 752 
 753     gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS;
 754 
 755     jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
 756     CHECK_NULL(inputEventClazz);
 757     jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
 758     CHECK_NULL(getButtonDownMasksID);
 759     jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
 760     CHECK_EXCEPTION();
 761     jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
 762     CHECK_NULL(tmp);
 763 
 764     gButtonDownMasks = (jint*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), gNumberOfButtons);
 765     if (gButtonDownMasks == NULL) {
 766         gNumberOfButtons = 0;
 767         (*env)->ReleaseIntArrayElements(env, obj, tmp, JNI_ABORT);
 768         JNU_ThrowOutOfMemoryError(env, NULL);
 769         return;
 770     }
 771 
 772     int i;
 773     for (i = 0; i < gNumberOfButtons; i++) {
 774         gButtonDownMasks[i] = tmp[i];
 775     }
 776 
 777     (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
 778     (*env)->DeleteLocalRef(env, obj);
 779 
 780     JNI_COCOA_EXIT(env);
 781 }
 782 
 783 /*
 784  * Class:     sun_lwawt_macosx_LWCToolkit
 785  * Method:    initAppkit
 786  * Signature: (Ljava/lang/ThreadGroup;)V
 787  */
 788 JNIEXPORT void JNICALL
 789 Java_sun_lwawt_macosx_LWCToolkit_initAppkit
 790 (JNIEnv *env, jclass klass, jobject appkitThreadGroup, jboolean headless) {
 791     JNI_COCOA_ENTER(env);
 792 
 793     [ThreadUtilities setAppkitThreadGroup:(*env)->NewGlobalRef(env, appkitThreadGroup)];
 794 
 795     // Launcher sets this env variable if -XstartOnFirstThread is specified
 796     char envVar[80];
 797     snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
 798     if (getenv(envVar) != NULL) {
 799         forceEmbeddedMode = YES;
 800         unsetenv(envVar);
 801     }
 802 
 803     if (isSWTInWebStart(env)) {
 804         forceEmbeddedMode = YES;
 805     }
 806 
 807     [AWTStarter start:headless ? YES : NO];
 808 
 809     JNI_COCOA_EXIT(env);
 810 }
 811 
 812 JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
 813     OSXAPP_SetJavaVM(vm);
 814 
 815     // We need to let Foundation know that this is a multithreaded application,
 816     // if it isn't already.
 817     if (![NSThread isMultiThreaded]) {
 818         [[[[NSThread alloc] init] autorelease] start];
 819     }
 820 
 821     return JNI_VERSION_1_4;
 822 }
 823 
 824 /*
 825  * Class:     sun_lwawt_macosx_LWCToolkit
 826  * Method:    isEmbedded
 827  * Signature: ()Z
 828  */
 829 JNIEXPORT jboolean JNICALL
 830 Java_sun_lwawt_macosx_LWCToolkit_isEmbedded
 831 (JNIEnv *env, jclass klass) {
 832     return isEmbedded ? JNI_TRUE : JNI_FALSE;
 833 }
 834 
 835 /*
 836  * Class:     sun_lwawt_macosx_LWCToolkit
 837  * Method:    isInAquaSession
 838  * Signature: ()Z
 839  */
 840 JNIEXPORT jboolean JNICALL
 841 Java_sun_lwawt_macosx_LWCToolkit_isInAquaSession
 842 (JNIEnv *env, jclass klass) {
 843     // copied from java.base/macosx/native/libjava/java_props_macosx.c
 844     // environment variable to bypass the aqua session check
 845     char *ev = getenv("AWT_FORCE_HEADFUL");
 846     if (ev && (strncasecmp(ev, "true", 4) == 0)) {
 847         // if "true" then tell the caller we're in an Aqua session without
 848         // actually checking
 849         return JNI_TRUE;
 850     }
 851     // Is the WindowServer available?
 852     SecuritySessionId session_id;
 853     SessionAttributeBits session_info;
 854     OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
 855     if (status == noErr) {
 856         if (session_info & sessionHasGraphicAccess) {
 857             return JNI_TRUE;
 858         }
 859     }
 860     return JNI_FALSE;
 861 }