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