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