1 /*
   2  * Copyright (c) 2011, 2013, 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 #import "sizecalc.h"
  41 
  42 int gNumberOfButtons;
  43 jint* gButtonDownMasks;
  44 
  45 @implementation AWTToolkit
  46 
  47 static long eventCount;
  48 
  49 + (long) getEventCount{
  50     return eventCount;
  51 }
  52 
  53 + (void) eventCountPlusPlus{    
  54     eventCount++;
  55 }
  56 
  57 @end
  58 
  59 
  60 @interface AWTRunLoopObject : NSObject {
  61     BOOL _shouldEndRunLoop;
  62 }
  63 @end
  64 
  65 @implementation AWTRunLoopObject
  66 
  67 - (id) init {
  68     self = [super init];
  69     if (self != nil) {
  70         _shouldEndRunLoop = NO;
  71     }
  72     return self;
  73 }
  74 
  75 - (BOOL) shouldEndRunLoop {
  76     return _shouldEndRunLoop;
  77 }
  78 
  79 - (void) endRunLoop {
  80     _shouldEndRunLoop = YES;
  81 }
  82 
  83 @end
  84 
  85 /*
  86  * Class:     sun_lwawt_macosx_LWCToolkit
  87  * Method:    nativeSyncQueue
  88  * Signature: (J)Z
  89  */
  90 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_nativeSyncQueue
  91 (JNIEnv *env, jobject self, jlong timeout)
  92 {
  93     int currentEventNum = [AWTToolkit getEventCount];
  94 
  95     NSApplication* sharedApp = [NSApplication sharedApplication];
  96     if ([sharedApp isKindOfClass:[NSApplicationAWT class]]) {
  97         NSApplicationAWT* theApp = (NSApplicationAWT*)sharedApp;
  98         [theApp postDummyEvent];
  99         [theApp waitForDummyEvent];
 100     } else {
 101         // could happen if we are embedded inside SWT application,
 102         // in this case just spin a single empty block through 
 103         // the event loop to give it a chance to process pending events
 104         [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){}];
 105     }
 106     
 107     if (([AWTToolkit getEventCount] - currentEventNum) != 0) {
 108         return JNI_TRUE;
 109     }
 110         
 111     return JNI_FALSE;
 112 }
 113 
 114 /*
 115  * Class:     sun_lwawt_macosx_LWCToolkit
 116  * Method:    flushNativeSelectors
 117  * Signature: ()J
 118  */
 119 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_flushNativeSelectors
 120 (JNIEnv *env, jclass clz)
 121 {
 122 JNF_COCOA_ENTER(env);
 123         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){}];
 124 JNF_COCOA_EXIT(env);
 125 }
 126 
 127 static JNF_CLASS_CACHE(jc_Component, "java/awt/Component");
 128 static JNF_MEMBER_CACHE(jf_Component_appContext, jc_Component, "appContext", "Lsun/awt/AppContext;");
 129 static JNF_CLASS_CACHE(jc_MenuComponent, "java/awt/MenuComponent");
 130 static JNF_MEMBER_CACHE(jf_MenuComponent_appContext, jc_MenuComponent, "appContext", "Lsun/awt/AppContext;");
 131 
 132 /*
 133  * Class:     sun_awt_SunToolkit
 134  * Method:    getAppContext
 135  * Signature: (Ljava/awt/Object;)Lsun/awt/AppContext;
 136  */
 137 JNIEXPORT jobject JNICALL
 138 Java_sun_awt_SunToolkit_getAppContext
 139 (JNIEnv *env, jclass cls, jobject obj)
 140 {
 141     jobject appContext = NULL;
 142 
 143 JNF_COCOA_ENTER(env);
 144 
 145     if (JNFIsInstanceOf(env, obj, &jc_Component)) {
 146         appContext = JNFGetObjectField(env, obj, jf_Component_appContext);
 147     } else if (JNFIsInstanceOf(env, obj, &jc_MenuComponent)) {
 148         appContext = JNFGetObjectField(env, obj, jf_MenuComponent_appContext);
 149     }
 150 
 151 JNF_COCOA_EXIT(env);
 152 
 153     return appContext;
 154 }
 155 
 156 /*
 157  * Class:     sun_awt_SunToolkit
 158  * Method:    setAppContext
 159  * Signature: (Ljava/lang/Object;Lsun/awt/AppContext;)Z
 160  */
 161 JNIEXPORT jboolean JNICALL
 162 Java_sun_awt_SunToolkit_setAppContext
 163 (JNIEnv *env, jclass cls, jobject obj, jobject appContext)
 164 {
 165     jboolean isComponent;
 166 
 167 JNF_COCOA_ENTER(env);
 168 
 169     if (JNFIsInstanceOf(env, obj, &jc_Component)) {
 170         JNFSetObjectField(env, obj, jf_Component_appContext, appContext);
 171         isComponent = JNI_TRUE;
 172     } else if (JNFIsInstanceOf(env, obj, &jc_MenuComponent)) {
 173         JNFSetObjectField(env, obj, jf_MenuComponent_appContext, appContext);
 174         isComponent = JNI_FALSE;
 175     }
 176 
 177 JNF_COCOA_EXIT(env);
 178 
 179     return isComponent;
 180 }
 181 
 182 /*
 183  * Class:     sun_lwawt_macosx_LWCToolkit
 184  * Method:    beep
 185  * Signature: ()V
 186  */
 187 JNIEXPORT void JNICALL
 188 Java_sun_lwawt_macosx_LWCToolkit_beep
 189 (JNIEnv *env, jobject self)
 190 {
 191     NSBeep(); // produces both sound and visual flash, if configured in System Preferences
 192 }
 193 
 194 /*
 195  * Class:     sun_lwawt_macosx_LWCToolkit
 196  * Method:    initIDs
 197  * Signature: ()V
 198  */
 199 JNIEXPORT void JNICALL
 200 Java_sun_lwawt_macosx_LWCToolkit_initIDs
 201 (JNIEnv *env, jclass klass) {
 202     // set thread names
 203     if (![ThreadUtilities isAWTEmbedded]) {
 204         dispatch_async(dispatch_get_main_queue(), ^(void){
 205             [[NSThread currentThread] setName:@"AppKit Thread"];
 206             JNIEnv *env = [ThreadUtilities getJNIEnv];
 207             static JNF_CLASS_CACHE(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
 208             static JNF_STATIC_MEMBER_CACHE(jsm_installToolkitThreadInJava, jc_LWCToolkit, "installToolkitThreadInJava", "()V");
 209             JNFCallStaticVoidMethod(env, jsm_installToolkitThreadInJava);
 210         });
 211     }
 212     
 213     gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS;
 214 
 215     jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
 216     CHECK_NULL(inputEventClazz);
 217     jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
 218     CHECK_NULL(getButtonDownMasksID);
 219     jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
 220     jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
 221     CHECK_NULL(tmp);
 222 
 223     gButtonDownMasks = (jint*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), gNumberOfButtons);
 224     if (gButtonDownMasks == NULL) {
 225         gNumberOfButtons = 0;
 226         (*env)->ReleaseIntArrayElements(env, obj, tmp, JNI_ABORT);
 227         JNU_ThrowOutOfMemoryError(env, NULL);
 228         return;
 229     }
 230 
 231     int i;
 232     for (i = 0; i < gNumberOfButtons; i++) {
 233         gButtonDownMasks[i] = tmp[i];
 234     }
 235 
 236     (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
 237     (*env)->DeleteLocalRef(env, obj);
 238 }
 239 
 240 static UInt32 RGB(NSColor *c) {
 241     c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
 242     if (c == nil)
 243     {
 244         return -1; // opaque white
 245     }
 246 
 247     CGFloat r, g, b, a;
 248     [c getRed:&r green:&g blue:&b alpha:&a];
 249 
 250     UInt32 ir = (UInt32) (r*255+0.5),
 251     ig = (UInt32) (g*255+0.5),
 252     ib = (UInt32) (b*255+0.5),
 253     ia = (UInt32) (a*255+0.5);
 254 
 255     //    NSLog(@"%@ %d, %d, %d", c, ir, ig, ib);
 256 
 257     return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0);
 258 }
 259 
 260 BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
 261     jint len = (*env)->GetArrayLength(env, jColors);
 262 
 263     UInt32 colorsArray[len];
 264     UInt32 *colors = colorsArray;
 265 
 266     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 267         NSUInteger i;
 268         for (i = 0; i < len; i++) {
 269             colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]);
 270         }
 271     }];
 272 
 273     jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0);
 274     if (_colors == NULL) {
 275         return NO;
 276     }
 277     memcpy(_colors, colors, len * sizeof(UInt32));
 278     (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0);
 279     return YES;
 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     if (doLoadNativeColors(env, jSystemColors, NO)) {
 292         doLoadNativeColors(env, jAppleColors, YES);
 293     }
 294 JNF_COCOA_EXIT(env);
 295 }
 296 
 297 /*
 298  * Class:     sun_lwawt_macosx_LWCToolkit
 299  * Method:    createAWTRunLoopMediator
 300  * Signature: ()J
 301  */
 302 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_LWCToolkit_createAWTRunLoopMediator
 303 (JNIEnv *env, jclass clz)
 304 {
 305 AWT_ASSERT_APPKIT_THREAD;
 306 
 307     AWTRunLoopObject *o = nil;
 308 
 309     // We double retain because this object is owned by both main thread and "other" thread
 310     // We release in both doAWTRunLoop and stopAWTRunLoop
 311     o = [[AWTRunLoopObject alloc] init];
 312     if (o) {
 313         CFRetain(o); // GC
 314         CFRetain(o); // GC
 315         [o release];
 316     }
 317     return ptr_to_jlong(o);
 318 }
 319 
 320 /*
 321  * Class:     sun_lwawt_macosx_LWCToolkit
 322  * Method:    doAWTRunLoopImpl
 323  * Signature: (JZZ)V
 324  */
 325 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoopImpl
 326 (JNIEnv *env, jclass clz, jlong mediator, jboolean processEvents, jboolean inAWT)
 327 {
 328 AWT_ASSERT_APPKIT_THREAD;
 329 JNF_COCOA_ENTER(env);
 330 
 331     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 332 
 333     if (mediatorObject == nil) return;
 334 
 335     // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 336     BOOL isRunning = true;
 337     while (![mediatorObject shouldEndRunLoop] && isRunning) {
 338         isRunning = [[NSRunLoop currentRunLoop] runMode:(inAWT ? [JNFRunLoop javaRunLoopMode] : NSDefaultRunLoopMode)
 339                                              beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]];
 340         if (processEvents) {
 341             //We do not spin a runloop here as date is nil, so does not matter which mode to use
 342             NSEvent *event;
 343             if ((event = [NSApp nextEventMatchingMask:NSAnyEventMask
 344                                            untilDate:nil
 345                                               inMode:NSDefaultRunLoopMode
 346                                              dequeue:YES]) != nil) {
 347                 [NSApp sendEvent:event];
 348             }
 349 
 350         }
 351     }
 352 
 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 JNF_COCOA_ENTER(env);
 368 
 369     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 370 
 371     [ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
 372 
 373     CFRelease(mediatorObject);
 374 
 375 JNF_COCOA_EXIT(env);
 376 }
 377 
 378 /*
 379  * Class:     sun_lwawt_macosx_LWCToolkit
 380  * Method:    isCapsLockOn
 381  * Signature: ()Z
 382  */
 383 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 384 (JNIEnv *env, jobject self)
 385 {
 386     __block jboolean isOn = JNI_FALSE;
 387     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 388         NSUInteger modifiers = [NSEvent modifierFlags];
 389         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 390     }];
 391 
 392     return isOn;
 393 }
 394 
 395 /*
 396  * Class:     sun_lwawt_macosx_LWCToolkit
 397  * Method:    isApplicationActive
 398  * Signature: ()Z
 399  */
 400 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 401 (JNIEnv *env, jclass clazz)
 402 {
 403     __block jboolean active = JNI_FALSE;
 404 
 405 JNF_COCOA_ENTER(env);
 406 
 407     [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
 408         active = (jboolean)[NSRunningApplication currentApplication].active;
 409     }];
 410 
 411 JNF_COCOA_EXIT(env);
 412 
 413     return active;
 414 }
 415 
 416 
 417 /*
 418  * Class:     sun_awt_SunToolkit
 419  * Method:    closeSplashScreen
 420  * Signature: ()V
 421  */
 422 JNIEXPORT void JNICALL
 423 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 424 {
 425     void *hSplashLib = dlopen(0, RTLD_LAZY);
 426     if (!hSplashLib) return;
 427 
 428     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 429     if (splashClose) {
 430         splashClose();
 431     }
 432     dlclose(hSplashLib);
 433 }
 434 
 435 
 436 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 437 // solaris tree)...
 438 
 439 JNIEXPORT jstring JNICALL
 440 Java_sun_font_FontManager_getFontPath
 441 (JNIEnv *env, jclass obj, jboolean noType1)
 442 {
 443     return JNFNSToJavaString(env, @"/Library/Fonts");
 444 }
 445 
 446 // This isn't yet used on unix, the implementation is added since shared
 447 // code calls this method in preparation for future use.
 448 JNIEXPORT void JNICALL
 449 Java_sun_font_FontManager_populateFontFileNameMap
 450 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 451 {
 452 
 453 }
 454