1 /*
   2  * Copyright (c) 2011, 2014, 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  * Class:     sun_lwawt_macosx_LWCToolkit
 149  * Method:    flushNativeSelectors
 150  * Signature: ()J
 151  */
 152 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_flushNativeSelectors
 153 (JNIEnv *env, jclass clz)
 154 {
 155 JNF_COCOA_ENTER(env);
 156         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){}];
 157 JNF_COCOA_EXIT(env);
 158 }
 159 
 160 /*
 161  * Class:     sun_lwawt_macosx_LWCToolkit
 162  * Method:    beep
 163  * Signature: ()V
 164  */
 165 JNIEXPORT void JNICALL
 166 Java_sun_lwawt_macosx_LWCToolkit_beep
 167 (JNIEnv *env, jobject self)
 168 {
 169     NSBeep(); // produces both sound and visual flash, if configured in System Preferences
 170 }
 171 
 172 /*
 173  * Class:     sun_lwawt_macosx_LWCToolkit
 174  * Method:    initIDs
 175  * Signature: ()V
 176  */
 177 JNIEXPORT void JNICALL
 178 Java_sun_lwawt_macosx_LWCToolkit_initIDs
 179 (JNIEnv *env, jclass klass) {
 180     // set thread names
 181     if (![ThreadUtilities isAWTEmbedded]) {
 182         dispatch_async(dispatch_get_main_queue(), ^(void){
 183             [[NSThread currentThread] setName:@"AppKit Thread"];
 184             JNIEnv *env = [ThreadUtilities getJNIEnv];
 185             static JNF_CLASS_CACHE(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit");
 186             static JNF_STATIC_MEMBER_CACHE(jsm_installToolkitThreadInJava, jc_LWCToolkit, "installToolkitThreadInJava", "()V");
 187             JNFCallStaticVoidMethod(env, jsm_installToolkitThreadInJava);
 188         });
 189     }
 190     
 191     gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS;
 192 
 193     jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent");
 194     CHECK_NULL(inputEventClazz);
 195     jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I");
 196     CHECK_NULL(getButtonDownMasksID);
 197     jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID);
 198     jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE);
 199     CHECK_NULL(tmp);
 200 
 201     gButtonDownMasks = (jint*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(jint), gNumberOfButtons);
 202     if (gButtonDownMasks == NULL) {
 203         gNumberOfButtons = 0;
 204         (*env)->ReleaseIntArrayElements(env, obj, tmp, JNI_ABORT);
 205         JNU_ThrowOutOfMemoryError(env, NULL);
 206         return;
 207     }
 208 
 209     int i;
 210     for (i = 0; i < gNumberOfButtons; i++) {
 211         gButtonDownMasks[i] = tmp[i];
 212     }
 213 
 214     (*env)->ReleaseIntArrayElements(env, obj, tmp, 0);
 215     (*env)->DeleteLocalRef(env, obj);
 216 }
 217 
 218 static UInt32 RGB(NSColor *c) {
 219     c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
 220     if (c == nil)
 221     {
 222         return -1; // opaque white
 223     }
 224 
 225     CGFloat r, g, b, a;
 226     [c getRed:&r green:&g blue:&b alpha:&a];
 227 
 228     UInt32 ir = (UInt32) (r*255+0.5),
 229     ig = (UInt32) (g*255+0.5),
 230     ib = (UInt32) (b*255+0.5),
 231     ia = (UInt32) (a*255+0.5);
 232 
 233     //    NSLog(@"%@ %d, %d, %d", c, ir, ig, ib);
 234 
 235     return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0);
 236 }
 237 
 238 BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
 239     jint len = (*env)->GetArrayLength(env, jColors);
 240 
 241     UInt32 colorsArray[len];
 242     UInt32 *colors = colorsArray;
 243 
 244     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 245         NSUInteger i;
 246         for (i = 0; i < len; i++) {
 247             colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]);
 248         }
 249     }];
 250 
 251     jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0);
 252     if (_colors == NULL) {
 253         return NO;
 254     }
 255     memcpy(_colors, colors, len * sizeof(UInt32));
 256     (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0);
 257     return YES;
 258 }
 259 
 260 /**
 261  * Class:     sun_lwawt_macosx_LWCToolkit
 262  * Method:    loadNativeColors
 263  * Signature: ([I[I)V
 264  */
 265 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_loadNativeColors
 266 (JNIEnv *env, jobject peer, jintArray jSystemColors, jintArray jAppleColors)
 267 {
 268 JNF_COCOA_ENTER(env);
 269     if (doLoadNativeColors(env, jSystemColors, NO)) {
 270         doLoadNativeColors(env, jAppleColors, YES);
 271     }
 272 JNF_COCOA_EXIT(env);
 273 }
 274 
 275 /*
 276  * Class:     sun_lwawt_macosx_LWCToolkit
 277  * Method:    createAWTRunLoopMediator
 278  * Signature: ()J
 279  */
 280 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_LWCToolkit_createAWTRunLoopMediator
 281 (JNIEnv *env, jclass clz)
 282 {
 283 AWT_ASSERT_APPKIT_THREAD;
 284 
 285     jlong result;
 286 
 287 JNF_COCOA_ENTER(env);
 288     // We double retain because this object is owned by both main thread and "other" thread
 289     // We release in both doAWTRunLoop and stopAWTRunLoop
 290     result = ptr_to_jlong([[[AWTRunLoopObject alloc] init] retain]);
 291 JNF_COCOA_EXIT(env);
 292 
 293     return result;
 294 }
 295 
 296 /*
 297  * Class:     sun_lwawt_macosx_LWCToolkit
 298  * Method:    doAWTRunLoopImpl
 299  * Signature: (JZZ)V
 300  */
 301 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoopImpl
 302 (JNIEnv *env, jclass clz, jlong mediator, jboolean processEvents, jboolean inAWT)
 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     // Don't use acceptInputForMode because that doesn't setup autorelease pools properly
 312     BOOL isRunning = true;
 313     while (![mediatorObject shouldEndRunLoop] && isRunning) {
 314         isRunning = [[NSRunLoop currentRunLoop] runMode:(inAWT ? [JNFRunLoop javaRunLoopMode] : NSDefaultRunLoopMode)
 315                                              beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]];
 316         if (processEvents) {
 317             //We do not spin a runloop here as date is nil, so does not matter which mode to use
 318             NSEvent *event;
 319             if ((event = [NSApp nextEventMatchingMask:NSAnyEventMask
 320                                            untilDate:nil
 321                                               inMode:NSDefaultRunLoopMode
 322                                              dequeue:YES]) != nil) {
 323                 [NSApp sendEvent:event];
 324             }
 325 
 326         }
 327     }
 328     [mediatorObject release];
 329 JNF_COCOA_EXIT(env);
 330 }
 331 
 332 /*
 333  * Class:     sun_lwawt_macosx_LWCToolkit
 334  * Method:    stopAWTRunLoop
 335  * Signature: (J)V
 336  */
 337 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop
 338 (JNIEnv *env, jclass clz, jlong mediator)
 339 {
 340 JNF_COCOA_ENTER(env);
 341 
 342     AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
 343 
 344     [ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
 345 
 346     [mediatorObject release];
 347 
 348 JNF_COCOA_EXIT(env);
 349 }
 350 
 351 /*
 352  * Class:     sun_lwawt_macosx_LWCToolkit
 353  * Method:    performOnMainThreadAfterDelay
 354  * Signature: (Ljava/lang/Runnable;J)V
 355  */
 356 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAfterDelay
 357 (JNIEnv *env, jclass clz, jobject runnable, jlong delay)
 358 {
 359 JNF_COCOA_ENTER(env);
 360     jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
 361     CHECK_NULL(gRunnable);
 362     [ThreadUtilities performOnMainThreadWaiting:NO block:^() {
 363         JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
 364         [performer performSelector:@selector(perform) withObject:nil afterDelay:(delay/1000.0)];
 365     }];
 366 JNF_COCOA_EXIT(env);
 367 }
 368 
 369 
 370 /*
 371  * Class:     sun_lwawt_macosx_LWCToolkit
 372  * Method:    isCapsLockOn
 373  * Signature: ()Z
 374  */
 375 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn
 376 (JNIEnv *env, jobject self)
 377 {
 378     __block jboolean isOn = JNI_FALSE;
 379     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 380         NSUInteger modifiers = [NSEvent modifierFlags];
 381         isOn = (modifiers & NSAlphaShiftKeyMask) != 0;
 382     }];
 383 
 384     return isOn;
 385 }
 386 
 387 /*
 388  * Class:     sun_lwawt_macosx_LWCToolkit
 389  * Method:    isApplicationActive
 390  * Signature: ()Z
 391  */
 392 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive
 393 (JNIEnv *env, jclass clazz)
 394 {
 395     __block jboolean active = JNI_FALSE;
 396 
 397 JNF_COCOA_ENTER(env);
 398 
 399     [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
 400         active = (jboolean)[NSRunningApplication currentApplication].active;
 401     }];
 402 
 403 JNF_COCOA_EXIT(env);
 404 
 405     return active;
 406 }
 407 
 408 
 409 /*
 410  * Class:     sun_awt_SunToolkit
 411  * Method:    closeSplashScreen
 412  * Signature: ()V
 413  */
 414 JNIEXPORT void JNICALL
 415 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
 416 {
 417     void *hSplashLib = dlopen(0, RTLD_LAZY);
 418     if (!hSplashLib) return;
 419 
 420     void (*splashClose)() = dlsym(hSplashLib, "SplashClose");
 421     if (splashClose) {
 422         splashClose();
 423     }
 424     dlclose(hSplashLib);
 425 }
 426 
 427 
 428 // TODO: definitely doesn't belong here (copied from fontpath.c in the
 429 // solaris tree)...
 430 
 431 JNIEXPORT jstring JNICALL
 432 Java_sun_font_FontManager_getFontPath
 433 (JNIEnv *env, jclass obj, jboolean noType1)
 434 {
 435     return JNFNSToJavaString(env, @"/Library/Fonts");
 436 }
 437 
 438 // This isn't yet used on unix, the implementation is added since shared
 439 // code calls this method in preparation for future use.
 440 JNIEXPORT void JNICALL
 441 Java_sun_font_FontManager_populateFontFileNameMap
 442 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 443 {
 444 
 445 }