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 processEvents)
 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     // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 345     BOOL isRunning = true;
 346     while (![mediatorObject shouldEndRunLoop] && isRunning) {
 347         isRunning = [[NSRunLoop currentRunLoop] runMode:[JNFRunLoop javaRunLoopMode]
 348                                              beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]];
 349         if (processEvents) {
 350             //We do not spin a runloop here as date is nil, so does not matter which mode to use
 351             NSEvent *event;
 352             if ((event = [NSApp nextEventMatchingMask:NSAnyEventMask
 353                                            untilDate:nil
 354                                               inMode:NSDefaultRunLoopMode
 355                                              dequeue:YES]) != nil) {
 356                 [NSApp sendEvent:event];
 357             }
 358 
 359         }
 360     }
 361 
 362    
 363     CFRelease(mediatorObject);
 364 
 365 JNF_COCOA_EXIT(env);
 366 }
 367 
 368 /*
 369  * Class:     sun_lwawt_macosx_LWCToolkit
 370  * Method:    stopAWTRunLoop
 371  * Signature: (J)V
 372  */
 373 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop
 374 (JNIEnv *env, jclass clz, jlong mediator)
 375 {
 376 AWT_ASSERT_NOT_APPKIT_THREAD;
 377 JNF_COCOA_ENTER(env);
 378 
 379     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 380 
 381     [ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
 382 
 383     CFRelease(mediatorObject);
 384 
 385 JNF_COCOA_EXIT(env);
 386 }
 387 
 388 /*
 389  * Class:     sun_lwawt_macosx_LWCToolkit
 390  * Method:    isCapsLockOn
 391  * Signature: ()Z
 392  */
 393 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 394 (JNIEnv *env, jobject self)
 395 {
 396     __block jboolean isOn = JNI_FALSE;
 397     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 398         NSUInteger modifiers = [NSEvent modifierFlags];
 399         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 400     }];
 401 
 402     return isOn;
 403 }
 404 
 405 /*
 406  * Class:     sun_lwawt_macosx_LWCToolkit
 407  * Method:    isApplicationActive
 408  * Signature: ()Z
 409  */
 410 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 411 (JNIEnv *env, jclass clazz)
 412 {
 413     __block jboolean active = JNI_FALSE;
 414 
 415 JNF_COCOA_ENTER(env);
 416 
 417     [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
 418         active = (jboolean)[NSRunningApplication currentApplication].active;
 419     }];
 420 
 421 JNF_COCOA_EXIT(env);
 422 
 423     return active;
 424 }
 425 
 426 
 427 /*
 428  * Class:     sun_awt_SunToolkit
 429  * Method:    closeSplashScreen
 430  * Signature: ()V
 431  */
 432 JNIEXPORT void JNICALL
 433 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 434 {
 435     void *hSplashLib = dlopen(0, RTLD_LAZY);
 436     if (!hSplashLib) return;
 437 
 438     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 439     if (splashClose) {
 440         splashClose();
 441     }
 442     dlclose(hSplashLib);
 443 }
 444 
 445 
 446 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 447 // solaris tree)...
 448 
 449 JNIEXPORT jstring JNICALL
 450 Java_sun_font_FontManager_getFontPath
 451 (JNIEnv *env, jclass obj, jboolean noType1)
 452 {
 453     return JNFNSToJavaString(env, @"/Library/Fonts");
 454 }
 455 
 456 // This isn't yet used on unix, the implementation is added since shared
 457 // code calls this method in preparation for future use.
 458 JNIEXPORT void JNICALL
 459 Java_sun_font_FontManager_populateFontFileNameMap
 460 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 461 {
 462 
 463 }
 464 
 465