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 /*
 181  * Class:     sun_lwawt_macosx_LWCToolkit
 182  * Method:    initIDs
 183  * Signature: ()V
 184  */
 185 JNIEXPORT void JNICALL
 186 Java_sun_lwawt_macosx_LWCToolkit_initIDs
 187 (JNIEnv *env, jclass klass) {
 188     // set thread names
 189     dispatch_async(dispatch_get_main_queue(), ^(void){
 190         [[NSThread currentThread] setName:@"AppKit Thread"];
 191 
 192         JNIEnv *env = [ThreadUtilities getJNIEnv];
 193         static JNF_CLASS_CACHE(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
 194         static JNF_STATIC_MEMBER_CACHE(jsm_installToolkitThreadNameInJava, jc_LWCToolkit, "installToolkitThreadNameInJava", "()V");
 195         JNFCallStaticVoidMethod(env, jsm_installToolkitThreadNameInJava);
 196     });
 197 
 198     gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS;
 199 
 200     jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
 201     jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
 202     jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
 203     jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
 204 
 205     gButtonDownMasks = (jint*)malloc(sizeof(jint) * gNumberOfButtons);
 206     if (gButtonDownMasks == NULL) {
 207         gNumberOfButtons = 0;
 208         JNU_ThrowOutOfMemoryError(env, NULL);
 209         return;
 210     }
 211 
 212     int i;
 213     for (i = 0; i < gNumberOfButtons; i++) {
 214         gButtonDownMasks[i] = tmp[i];
 215     }
 216 
 217     (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
 218     (*env)->DeleteLocalRef(env, obj);
 219 }
 220 
 221 static UInt32 RGB(NSColor *c) {
 222     c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
 223     if (c == nil)
 224     {
 225         return -1; // opaque white
 226     }
 227 
 228     CGFloat r, g, b, a;
 229     [c getRed:&r green:&g blue:&b alpha:&a];
 230 
 231     UInt32 ir = (UInt32) (r*255+0.5),
 232     ig = (UInt32) (g*255+0.5),
 233     ib = (UInt32) (b*255+0.5),
 234     ia = (UInt32) (a*255+0.5);
 235 
 236     //    NSLog(@"%@ %d, %d, %d", c, ir, ig, ib);
 237 
 238     return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0);
 239 }
 240 
 241 void doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
 242     jint len = (*env)->GetArrayLength(env, jColors);
 243 
 244     UInt32 colorsArray[len];
 245     UInt32 *colors = colorsArray;
 246 
 247     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 248         NSUInteger i;
 249         for (i = 0; i < len; i++) {
 250             colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]);
 251         }
 252     }];
 253 
 254     jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0);
 255     memcpy(_colors, colors, len * sizeof(UInt32));
 256     (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0);
 257 }
 258 
 259 /**
 260  * Class:     sun_lwawt_macosx_LWCToolkit
 261  * Method:    loadNativeColors
 262  * Signature: ([I[I)V
 263  */
 264 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_loadNativeColors
 265 (JNIEnv *env, jobject peer, jintArray jSystemColors, jintArray jAppleColors)
 266 {
 267 JNF_COCOA_ENTER(env);
 268     doLoadNativeColors(env, jSystemColors, NO);
 269     doLoadNativeColors(env, jAppleColors, YES);
 270 JNF_COCOA_EXIT(env);
 271 }
 272 
 273 /*
 274  * Class:     sun_lwawt_macosx_LWCToolkit
 275  * Method:    createAWTRunLoopMediator
 276  * Signature: ()J
 277  */
 278 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_LWCToolkit_createAWTRunLoopMediator
 279 (JNIEnv *env, jclass clz)
 280 {
 281 AWT_ASSERT_APPKIT_THREAD;
 282 
 283     AWTRunLoopObject *o = nil;
 284 
 285     // We double retain because this object is owned by both main thread and "other" thread
 286     // We release in both doAWTRunLoop and stopAWTRunLoop
 287     o = [[AWTRunLoopObject alloc] init];
 288     if (o) {
 289         CFRetain(o); // GC
 290         CFRetain(o); // GC
 291         [o release];
 292     }
 293     return ptr_to_jlong(o);
 294 }
 295 
 296 /*
 297  * Class:     sun_lwawt_macosx_LWCToolkit
 298  * Method:    doAWTRunLoop
 299  * Signature: (JZZ)V
 300  */
 301 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoop
 302 (JNIEnv *env, jclass clz, jlong mediator, jboolean awtMode, jboolean detectDeadlocks)
 303 {
 304 AWT_ASSERT_APPKIT_THREAD;
 305 JNF_COCOA_ENTER(env);
 306 
 307     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 308 
 309     if (mediatorObject == nil) return;
 310 
 311     if (!sInPerformFromJava || !detectDeadlocks) {
 312 
 313         NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
 314         NSDate *distantFuture = [NSDate distantFuture];
 315         NSString *mode = (awtMode) ? [JNFRunLoop javaRunLoopMode] : NSDefaultRunLoopMode;
 316 
 317         BOOL isRunning = YES;
 318         while (isRunning && ![mediatorObject shouldEndRunLoop]) {
 319             // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 320             isRunning = [currentRunLoop runMode:mode beforeDate:distantFuture];
 321         }
 322 
 323     }
 324 #ifndef PRODUCT_BUILD
 325     if (sInPerformFromJava) {
 326         NSLog(@"Apple AWT: Short-circuiting CToolkit.invokeAndWait trampoline deadlock!!!!!");
 327         NSLog(@"\tPlease file a bug report with this message and a reproducible test case.");
 328     }
 329 #endif
 330 
 331     CFRelease(mediatorObject);
 332 
 333 JNF_COCOA_EXIT(env);
 334 }
 335 
 336 /*
 337  * Class:     sun_lwawt_macosx_LWCToolkit
 338  * Method:    stopAWTRunLoop
 339  * Signature: (J)V
 340  */
 341 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop
 342 (JNIEnv *env, jclass clz, jlong mediator)
 343 {
 344 AWT_ASSERT_NOT_APPKIT_THREAD;
 345 JNF_COCOA_ENTER(env);
 346 
 347     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 348 
 349     [ThreadUtilities performOnMainThread:@selector(endRunLoop) onObject:mediatorObject withObject:nil waitUntilDone:NO awtMode:YES];
 350 
 351     CFRelease(mediatorObject);
 352 
 353 JNF_COCOA_EXIT(env);
 354 }
 355 
 356 /*
 357  * Class:     sun_lwawt_macosx_LWCToolkit
 358  * Method:    isCapsLockOn
 359  * Signature: ()Z
 360  */
 361 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 362 (JNIEnv *env, jobject self)
 363 {
 364     __block jboolean isOn = JNI_FALSE;
 365     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 366         NSUInteger modifiers = [NSEvent modifierFlags];
 367         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 368     }];
 369 
 370     return isOn;
 371 }
 372 
 373 /*
 374  * Class:     sun_lwawt_macosx_LWCToolkit
 375  * Method:    isApplicationActive
 376  * Signature: ()Z
 377  */
 378 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 379 (JNIEnv *env, jclass clazz)
 380 {
 381     __block jboolean active = JNI_FALSE;
 382 
 383 JNF_COCOA_ENTER(env);
 384 
 385     [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
 386         active = (jboolean)[NSRunningApplication currentApplication].active;
 387     }];
 388 
 389 JNF_COCOA_EXIT(env);
 390 
 391     return active;
 392 }
 393 
 394 
 395 /*
 396  * Class:     sun_awt_SunToolkit
 397  * Method:    closeSplashScreen
 398  * Signature: ()V
 399  */
 400 JNIEXPORT void JNICALL
 401 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 402 {
 403     void *hSplashLib = dlopen(0, RTLD_LAZY);
 404     if (!hSplashLib) return;
 405 
 406     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 407     if (splashClose) {
 408         splashClose();
 409     }
 410     dlclose(hSplashLib);
 411 }
 412 
 413 
 414 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 415 // solaris tree)...
 416 
 417 JNIEXPORT jstring JNICALL
 418 Java_sun_font_FontManager_getFontPath
 419 (JNIEnv *env, jclass obj, jboolean noType1)
 420 {
 421     return JNFNSToJavaString(env, @"/Library/Fonts");
 422 }
 423 
 424 // This isn't yet used on unix, the implementation is added since shared
 425 // code calls this method in preparation for future use.
 426 JNIEXPORT void JNICALL
 427 Java_sun_font_FontManager_populateFontFileNameMap
 428 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 429 {
 430 
 431 }
 432 
 433 /*
 434  * Class:     sun_lwawt_macosx_LWCToolkit
 435  * Method:    executeNextAppKitEvent
 436  * Signature: ()V
 437  */
 438 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_executeNextAppKitEvent
 439 (JNIEnv *env, jclass cls)
 440 {
 441     // Simply get the next event in native loop and pass it to execution
 442     // We'll be called repeatedly so there's no need to block here
 443     NSRunLoop *theRL = [NSRunLoop currentRunLoop];
 444     NSApplication * app = [NSApplication sharedApplication];
 445     NSEvent * event = [app nextEventMatchingMask: 0xFFFFFFFF untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
 446     if (event != nil) {
 447         [app sendEvent: event];
 448     }
 449 }