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