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