1 /*
   2  * Copyright (c) 2011, 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 <JavaNativeFoundation/JavaNativeFoundation.h>
  28 
  29 #include "jni_util.h"
  30 #import "CMenuBar.h"
  31 #import "InitIDs.h"
  32 #import "LWCToolkit.h"
  33 #import "ThreadUtilities.h"
  34 #import "AWT_debug.h"
  35 #import "CSystemColors.h"
  36 
  37 #import "sun_lwawt_macosx_LWCToolkit.h"
  38 
  39 int gNumberOfButtons;
  40 jint* gButtonDownMasks;
  41 
  42 @implementation AWTToolkit
  43 
  44 static long eventCount;
  45 static bool shouldKeepRunningNestedLoop = NO;
  46 
  47 + (long) getEventCount{
  48     return eventCount;
  49 }
  50 
  51 + (void) eventCountPlusPlus{
  52     eventCount++;
  53 }
  54 
  55 @end
  56 
  57 
  58 @interface AWTRunLoopObject : NSObject {
  59     BOOL _shouldEndRunLoop;
  60 }
  61 @end
  62 
  63 @implementation AWTRunLoopObject
  64 
  65 - (id) init {
  66     self = [super init];
  67     if (self != nil) {
  68         _shouldEndRunLoop = NO;
  69     }
  70     return self;
  71 }
  72 
  73 - (BOOL) shouldEndRunLoop {
  74     return _shouldEndRunLoop;
  75 }
  76 
  77 - (void) endRunLoop {
  78     _shouldEndRunLoop = YES;
  79 }
  80 
  81 @end
  82 
  83 
  84 /*
  85  * Class:     sun_lwawt_macosx_LWCToolkit
  86  * Method:    nativeSyncQueue
  87  * Signature: (J)Z
  88  */
  89 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_nativeSyncQueue
  90 (JNIEnv *env, jobject self, jlong timeout)
  91 {
  92     int currentEventNum = [AWTToolkit getEventCount];
  93 
  94     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){}];
  95 
  96     if (([AWTToolkit getEventCount] - currentEventNum) != 0) {
  97         return JNI_TRUE;
  98     }
  99 
 100     return JNI_FALSE;
 101 }
 102 
 103 
 104 static JNF_CLASS_CACHE(jc_Component, "java/awt/Component");
 105 static JNF_MEMBER_CACHE(jf_Component_appContext, jc_Component, "appContext", "Lsun/awt/AppContext;");
 106 static JNF_CLASS_CACHE(jc_MenuComponent, "java/awt/MenuComponent");
 107 static JNF_MEMBER_CACHE(jf_MenuComponent_appContext, jc_MenuComponent, "appContext", "Lsun/awt/AppContext;");
 108 
 109 /*
 110  * Class:     sun_awt_SunToolkit
 111  * Method:    getAppContext
 112  * Signature: (Ljava/awt/Object;)Lsun/awt/AppContext;
 113  */
 114 JNIEXPORT jobject JNICALL
 115 Java_sun_awt_SunToolkit_getAppContext
 116 (JNIEnv *env, jclass cls, jobject obj)
 117 {
 118     jobject appContext = NULL;
 119 
 120 JNF_COCOA_ENTER(env);
 121 
 122     if (JNFIsInstanceOf(env, obj, &jc_Component)) {
 123         appContext = JNFGetObjectField(env, obj, jf_Component_appContext);
 124     } else if (JNFIsInstanceOf(env, obj, &jc_MenuComponent)) {
 125         appContext = JNFGetObjectField(env, obj, jf_MenuComponent_appContext);
 126     }
 127 
 128 JNF_COCOA_EXIT(env);
 129 
 130     return appContext;
 131 }
 132 
 133 /*
 134  * Class:     sun_awt_SunToolkit
 135  * Method:    setAppContext
 136  * Signature: (Ljava/lang/Object;Lsun/awt/AppContext;)Z
 137  */
 138 JNIEXPORT jboolean JNICALL
 139 Java_sun_awt_SunToolkit_setAppContext
 140 (JNIEnv *env, jclass cls, jobject obj, jobject appContext)
 141 {
 142     jboolean isComponent;
 143 
 144 JNF_COCOA_ENTER(env);
 145 
 146     if (JNFIsInstanceOf(env, obj, &jc_Component)) {
 147         JNFSetObjectField(env, obj, jf_Component_appContext, appContext);
 148         isComponent = JNI_TRUE;
 149     } else if (JNFIsInstanceOf(env, obj, &jc_MenuComponent)) {
 150         JNFSetObjectField(env, obj, jf_MenuComponent_appContext, appContext);
 151         isComponent = JNI_FALSE;
 152     }
 153 
 154 JNF_COCOA_EXIT(env);
 155 
 156     return isComponent;
 157 }
 158 
 159 /*
 160  * Class:     sun_lwawt_macosx_LWCToolkit
 161  * Method:    beep
 162  * Signature: ()V
 163  */
 164 JNIEXPORT void JNICALL
 165 Java_sun_lwawt_macosx_LWCToolkit_beep
 166 (JNIEnv *env, jobject self)
 167 {
 168     NSBeep(); // produces both sound and visual flash, if configured in System Preferences
 169 }
 170 
 171 CGDirectDisplayID
 172 FindCGDirectDisplayIDForScreenIndex(jint screenIndex)
 173 {
 174     // most common case - just one monitor
 175     CGDirectDisplayID screenID = CGMainDisplayID();
 176 
 177     CGDisplayCount displayCount = 0;
 178     CGGetOnlineDisplayList(0, NULL, &displayCount);
 179 
 180     if ((displayCount > 1) &&
 181         (screenIndex >= 0) &&
 182         (screenIndex < (jint)displayCount))
 183     {
 184         if (displayCount < 10) {
 185             // stack allocated optimization for less than 10 monitors
 186             CGDirectDisplayID onlineDisplays[displayCount];
 187             CGGetOnlineDisplayList(displayCount, onlineDisplays, &displayCount);
 188             screenID = (CGDirectDisplayID)onlineDisplays[screenIndex];
 189         } else {
 190             CGDirectDisplayID *onlineDisplays =
 191             malloc(displayCount*sizeof(CGDirectDisplayID));
 192             if (onlineDisplays != NULL) {
 193                 CGGetOnlineDisplayList(displayCount, onlineDisplays,
 194                                        &displayCount);
 195                 screenID = (CGDirectDisplayID)onlineDisplays[screenIndex];
 196                 free(onlineDisplays);
 197             }
 198         }
 199     }
 200 
 201     return screenID;
 202 }
 203 
 204 /*
 205  * Class:     sun_lwawt_macosx_LWCToolkit
 206  * Method:    initIDs
 207  * Signature: ()V
 208  */
 209 JNIEXPORT void JNICALL
 210 Java_sun_lwawt_macosx_LWCToolkit_initIDs
 211 (JNIEnv *env, jclass klass) {
 212     // set thread names
 213     dispatch_async(dispatch_get_main_queue(), ^(void){
 214         [[NSThread currentThread] setName:@"AppKit Thread"];
 215 
 216         JNIEnv *env = [ThreadUtilities getJNIEnv];
 217         static JNF_CLASS_CACHE(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
 218         static JNF_STATIC_MEMBER_CACHE(jsm_installToolkitThreadNameInJava, jc_LWCToolkit, "installToolkitThreadNameInJava", "()V");
 219         JNFCallStaticVoidMethod(env, jsm_installToolkitThreadNameInJava);
 220     });
 221     
 222     gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS;
 223     
 224     jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
 225     jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
 226     jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
 227     jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
 228     
 229     gButtonDownMasks = (jint*)malloc(sizeof(jint) * gNumberOfButtons);
 230     if (gButtonDownMasks == NULL) {
 231         gNumberOfButtons = 0;
 232         JNU_ThrowOutOfMemoryError(env, NULL);
 233         return;
 234     }
 235     
 236     int i;
 237     for (i = 0; i < gNumberOfButtons; i++) {
 238         gButtonDownMasks[i] = tmp[i];
 239     }
 240     
 241     (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
 242     (*env)->DeleteLocalRef(env, obj);
 243 }
 244 
 245 static UInt32 RGB(NSColor *c) {
 246     c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
 247     if (c == nil)
 248     {
 249         return -1; // opaque white
 250     }
 251 
 252     CGFloat r, g, b, a;
 253     [c getRed:&r green:&g blue:&b alpha:&a];
 254 
 255     UInt32 ir = (UInt32) (r*255+0.5),
 256     ig = (UInt32) (g*255+0.5),
 257     ib = (UInt32) (b*255+0.5),
 258     ia = (UInt32) (a*255+0.5);
 259 
 260     //    NSLog(@"%@ %d, %d, %d", c, ir, ig, ib);
 261 
 262     return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0);
 263 }
 264 
 265 void doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
 266     jint len = (*env)->GetArrayLength(env, jColors);
 267 
 268     UInt32 colorsArray[len];
 269     UInt32 *colors = colorsArray;
 270 
 271     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 272         NSUInteger i;
 273         for (i = 0; i < len; i++) {
 274             colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]);
 275         }
 276     }];
 277 
 278     jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0);
 279     memcpy(_colors, colors, len * sizeof(UInt32));
 280     (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0);
 281 }
 282 
 283 /**
 284  * Class:     sun_lwawt_macosx_LWCToolkit
 285  * Method:    loadNativeColors
 286  * Signature: ([I[I)V
 287  */
 288 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_loadNativeColors
 289 (JNIEnv *env, jobject peer, jintArray jSystemColors, jintArray jAppleColors)
 290 {
 291 JNF_COCOA_ENTER(env);
 292     doLoadNativeColors(env, jSystemColors, NO);
 293     doLoadNativeColors(env, jAppleColors, YES);
 294 JNF_COCOA_EXIT(env);
 295 }
 296 
 297 /*
 298  * Class:     sun_lwawt_macosx_LWCToolkit
 299  * Method:    createAWTRunLoopMediator
 300  * Signature: ()J
 301  */
 302 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_LWCToolkit_createAWTRunLoopMediator
 303 (JNIEnv *env, jclass clz)
 304 {
 305 AWT_ASSERT_APPKIT_THREAD;
 306 
 307     AWTRunLoopObject *o = nil;
 308 
 309     // We double retain because this object is owned by both main thread and "other" thread
 310     // We release in both doAWTRunLoop and stopAWTRunLoop
 311     o = [[AWTRunLoopObject alloc] init];
 312     if (o) {
 313         CFRetain(o); // GC
 314         CFRetain(o); // GC
 315         [o release];
 316     }
 317     return ptr_to_jlong(o);
 318 }
 319 
 320 /*
 321  * Class:     sun_lwawt_macosx_LWCToolkit
 322  * Method:    doAWTRunLoop
 323  * Signature: (JZZ)V
 324  */
 325 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoop
 326 (JNIEnv *env, jclass clz, jlong mediator, jboolean awtMode, jboolean detectDeadlocks)
 327 {
 328 AWT_ASSERT_APPKIT_THREAD;
 329 JNF_COCOA_ENTER(env);
 330 
 331     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 332 
 333     if (mediatorObject == nil) return;
 334 
 335     if (!sInPerformFromJava || !detectDeadlocks) {
 336 
 337         NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
 338         NSDate *distantFuture = [NSDate distantFuture];
 339         NSString *mode = (awtMode) ? [JNFRunLoop javaRunLoopMode] : NSDefaultRunLoopMode;
 340 
 341         BOOL isRunning = YES;
 342         while (isRunning && ![mediatorObject shouldEndRunLoop]) {
 343             // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 344             isRunning = [currentRunLoop runMode:mode beforeDate:distantFuture];
 345         }
 346 
 347     }
 348 #ifndef PRODUCT_BUILD
 349     if (sInPerformFromJava) {
 350         NSLog(@"Apple AWT: Short-circuiting CToolkit.invokeAndWait trampoline deadlock!!!!!");
 351         NSLog(@"\tPlease file a bug report with this message and a reproducible test case.");
 352     }
 353 #endif
 354 
 355     CFRelease(mediatorObject);
 356 
 357 JNF_COCOA_EXIT(env);
 358 }
 359 
 360 /*
 361  * Class:     sun_lwawt_macosx_LWCToolkit
 362  * Method:    stopAWTRunLoop
 363  * Signature: (J)V
 364  */
 365 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop
 366 (JNIEnv *env, jclass clz, jlong mediator)
 367 {
 368 AWT_ASSERT_NOT_APPKIT_THREAD;
 369 JNF_COCOA_ENTER(env);
 370 
 371     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 372 
 373     [ThreadUtilities performOnMainThread:@selector(endRunLoop) onObject:mediatorObject withObject:nil waitUntilDone:NO awtMode:YES];
 374 
 375     CFRelease(mediatorObject);
 376 
 377 JNF_COCOA_EXIT(env);
 378 }
 379 
 380 /*
 381  * Class:     sun_lwawt_macosx_LWCToolkit
 382  * Method:    isCapsLockOn
 383  * Signature: ()Z
 384  */
 385 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 386 (JNIEnv *env, jobject self)
 387 {
 388     __block jboolean isOn = JNI_FALSE;
 389     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 390         NSUInteger modifiers = [NSEvent modifierFlags];
 391         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 392     }];
 393 
 394     return isOn;
 395 }
 396 
 397 /*
 398  * Class:     sun_lwawt_macosx_LWCToolkit
 399  * Method:    isApplicationActive
 400  * Signature: ()Z
 401  */
 402 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 403 (JNIEnv *env, jclass clazz)
 404 {
 405     __block jboolean active = JNI_FALSE;
 406 
 407 JNF_COCOA_ENTER(env);
 408 
 409     if ([NSThread isMainThread]) {
 410         active = (jboolean)[NSRunningApplication currentApplication].active;
 411     } else {
 412         [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^() {
 413             active = (jboolean)[NSRunningApplication currentApplication].active;
 414         }];
 415     }
 416 
 417 JNF_COCOA_EXIT(env);
 418 
 419     return active;
 420 }
 421 
 422 
 423 /*
 424  * Class:     sun_awt_SunToolkit
 425  * Method:    closeSplashScreen
 426  * Signature: ()V
 427  */
 428 JNIEXPORT void JNICALL
 429 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 430 {
 431     void *hSplashLib = dlopen(0, RTLD_LAZY);
 432     if (!hSplashLib) return;
 433 
 434     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 435     if (splashClose) {
 436         splashClose();
 437     }
 438     dlclose(hSplashLib);
 439 }
 440 
 441 
 442 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 443 // solaris tree)...
 444 
 445 JNIEXPORT jstring JNICALL
 446 Java_sun_font_FontManager_getFontPath
 447 (JNIEnv *env, jclass obj, jboolean noType1)
 448 {
 449     return JNFNSToJavaString(env, @"/Library/Fonts");
 450 }
 451 
 452 // This isn't yet used on unix, the implementation is added since shared
 453 // code calls this method in preparation for future use.
 454 JNIEXPORT void JNICALL
 455 Java_sun_font_FontManager_populateFontFileNameMap
 456 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 457 {
 458 
 459 }
 460 
 461 /*
 462  * Class:     sun_lwawt_macosx_LWCToolkit
 463  * Method:    startNativeNestedEventLoop
 464  * Signature: ()V
 465  */
 466 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_startNativeNestedEventLoop
 467 (JNIEnv *env, jclass cls)
 468 {
 469     if(!shouldKeepRunningNestedLoop) {
 470         NSRunLoop *theRL = [NSRunLoop currentRunLoop];
 471         NSApplication * app = [NSApplication sharedApplication];
 472         shouldKeepRunningNestedLoop = YES;
 473         while (shouldKeepRunningNestedLoop && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
 474         {
 475             NSEvent * event = [app nextEventMatchingMask: 0xFFFFFFFF untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
 476             if (event != nil) {
 477                 [app sendEvent: event];
 478             }
 479         }
 480     }
 481 }
 482 
 483 /*
 484  * Class:     sun_lwawt_macosx_LWCToolkit
 485  * Method:    stopNativeNestedEventLoop
 486  * Signature: ()V
 487  */
 488 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopNativeNestedEventLoop
 489 (JNIEnv *env, jclass cls)
 490 {
 491     shouldKeepRunningNestedLoop = NO;
 492 }