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