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