1 /* 2 * Copyright (c) 2011, 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 37 #import "sun_lwawt_macosx_LWCToolkit.h" 38 39 int gNumberOfButtons; 40 jint* gButtonDownMasks; 41 42 @implementation AWTToolkit 43 44 static long eventCount; 45 static bool shouldKeepRunningNestedLoop = NO; 46 47 + (long) getEventCount{ 48 return eventCount; 49 } 50 51 + (void) eventCountPlusPlus{ 52 eventCount++; 53 } 54 55 @end 56 57 58 @interface AWTRunLoopObject : NSObject { 59 BOOL _shouldEndRunLoop; 60 } 61 @end 62 63 @implementation AWTRunLoopObject 64 65 - (id) init { 66 self = [super init]; 67 if (self != nil) { 68 _shouldEndRunLoop = NO; 69 } 70 return self; 71 } 72 73 - (BOOL) shouldEndRunLoop { 74 return _shouldEndRunLoop; 75 } 76 77 - (void) endRunLoop { 78 _shouldEndRunLoop = YES; 79 } 80 81 @end 82 83 84 /* 85 * Class: sun_lwawt_macosx_LWCToolkit 86 * Method: nativeSyncQueue 87 * Signature: (J)Z 88 */ 89 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_nativeSyncQueue 90 (JNIEnv *env, jobject self, jlong timeout) 91 { 92 int currentEventNum = [AWTToolkit getEventCount]; 93 94 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){}]; 95 96 if (([AWTToolkit getEventCount] - currentEventNum) != 0) { 97 return JNI_TRUE; 98 } 99 100 return JNI_FALSE; 101 } 102 103 104 static JNF_CLASS_CACHE(jc_Component, "java/awt/Component"); 105 static JNF_MEMBER_CACHE(jf_Component_appContext, jc_Component, "appContext", "Lsun/awt/AppContext;"); 106 static JNF_CLASS_CACHE(jc_MenuComponent, "java/awt/MenuComponent"); 107 static JNF_MEMBER_CACHE(jf_MenuComponent_appContext, jc_MenuComponent, "appContext", "Lsun/awt/AppContext;"); 108 109 /* 110 * Class: sun_awt_SunToolkit 111 * Method: getAppContext 112 * Signature: (Ljava/awt/Object;)Lsun/awt/AppContext; 113 */ 114 JNIEXPORT jobject JNICALL 115 Java_sun_awt_SunToolkit_getAppContext 116 (JNIEnv *env, jclass cls, jobject obj) 117 { 118 jobject appContext = NULL; 119 120 JNF_COCOA_ENTER(env); 121 122 if (JNFIsInstanceOf(env, obj, &jc_Component)) { 123 appContext = JNFGetObjectField(env, obj, jf_Component_appContext); 124 } else if (JNFIsInstanceOf(env, obj, &jc_MenuComponent)) { 125 appContext = JNFGetObjectField(env, obj, jf_MenuComponent_appContext); 126 } 127 128 JNF_COCOA_EXIT(env); 129 130 return appContext; 131 } 132 133 /* 134 * Class: sun_awt_SunToolkit 135 * Method: setAppContext 136 * Signature: (Ljava/lang/Object;Lsun/awt/AppContext;)Z 137 */ 138 JNIEXPORT jboolean JNICALL 139 Java_sun_awt_SunToolkit_setAppContext 140 (JNIEnv *env, jclass cls, jobject obj, jobject appContext) 141 { 142 jboolean isComponent; 143 144 JNF_COCOA_ENTER(env); 145 146 if (JNFIsInstanceOf(env, obj, &jc_Component)) { 147 JNFSetObjectField(env, obj, jf_Component_appContext, appContext); 148 isComponent = JNI_TRUE; 149 } else if (JNFIsInstanceOf(env, obj, &jc_MenuComponent)) { 150 JNFSetObjectField(env, obj, jf_MenuComponent_appContext, appContext); 151 isComponent = JNI_FALSE; 152 } 153 154 JNF_COCOA_EXIT(env); 155 156 return isComponent; 157 } 158 159 /* 160 * Class: sun_lwawt_macosx_LWCToolkit 161 * Method: beep 162 * Signature: ()V 163 */ 164 JNIEXPORT void JNICALL 165 Java_sun_lwawt_macosx_LWCToolkit_beep 166 (JNIEnv *env, jobject self) 167 { 168 NSBeep(); // produces both sound and visual flash, if configured in System Preferences 169 } 170 171 CGDirectDisplayID 172 FindCGDirectDisplayIDForScreenIndex(jint screenIndex) 173 { 174 // most common case - just one monitor 175 CGDirectDisplayID screenID = CGMainDisplayID(); 176 177 CGDisplayCount displayCount = 0; 178 CGGetOnlineDisplayList(0, NULL, &displayCount); 179 180 if ((displayCount > 1) && 181 (screenIndex >= 0) && 182 (screenIndex < (jint)displayCount)) 183 { 184 if (displayCount < 10) { 185 // stack allocated optimization for less than 10 monitors 186 CGDirectDisplayID onlineDisplays[displayCount]; 187 CGGetOnlineDisplayList(displayCount, onlineDisplays, &displayCount); 188 screenID = (CGDirectDisplayID)onlineDisplays[screenIndex]; 189 } else { 190 CGDirectDisplayID *onlineDisplays = 191 malloc(displayCount*sizeof(CGDirectDisplayID)); 192 if (onlineDisplays != NULL) { 193 CGGetOnlineDisplayList(displayCount, onlineDisplays, 194 &displayCount); 195 screenID = (CGDirectDisplayID)onlineDisplays[screenIndex]; 196 free(onlineDisplays); 197 } 198 } 199 } 200 201 return screenID; 202 } 203 204 /* 205 * Class: sun_lwawt_macosx_LWCToolkit 206 * Method: initIDs 207 * Signature: ()V 208 */ 209 JNIEXPORT void JNICALL 210 Java_sun_lwawt_macosx_LWCToolkit_initIDs 211 (JNIEnv *env, jclass klass) { 212 // set thread names 213 dispatch_async(dispatch_get_main_queue(), ^(void){ 214 [[NSThread currentThread] setName:@"AppKit Thread"]; 215 216 JNIEnv *env = [ThreadUtilities getJNIEnv]; 217 static JNF_CLASS_CACHE(jc_LWCToolkit, "sun/lwawt/macosx/LWCToolkit"); 218 static JNF_STATIC_MEMBER_CACHE(jsm_installToolkitThreadNameInJava, jc_LWCToolkit, "installToolkitThreadNameInJava", "()V"); 219 JNFCallStaticVoidMethod(env, jsm_installToolkitThreadNameInJava); 220 }); 221 222 gNumberOfButtons = sun_lwawt_macosx_LWCToolkit_BUTTONS; 223 224 jclass inputEventClazz = (*env)->FindClass(env, "java/awt/event/InputEvent"); 225 jmethodID getButtonDownMasksID = (*env)->GetStaticMethodID(env, inputEventClazz, "getButtonDownMasks", "()[I"); 226 jintArray obj = (jintArray)(*env)->CallStaticObjectMethod(env, inputEventClazz, getButtonDownMasksID); 227 jint * tmp = (*env)->GetIntArrayElements(env, obj, JNI_FALSE); 228 229 gButtonDownMasks = (jint*)malloc(sizeof(jint) * gNumberOfButtons); 230 if (gButtonDownMasks == NULL) { 231 gNumberOfButtons = 0; 232 JNU_ThrowOutOfMemoryError(env, NULL); 233 return; 234 } 235 236 int i; 237 for (i = 0; i < gNumberOfButtons; i++) { 238 gButtonDownMasks[i] = tmp[i]; 239 } 240 241 (*env)->ReleaseIntArrayElements(env, obj, tmp, 0); 242 (*env)->DeleteLocalRef(env, obj); 243 } 244 245 static UInt32 RGB(NSColor *c) { 246 c = [c colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; 247 if (c == nil) 248 { 249 return -1; // opaque white 250 } 251 252 CGFloat r, g, b, a; 253 [c getRed:&r green:&g blue:&b alpha:&a]; 254 255 UInt32 ir = (UInt32) (r*255+0.5), 256 ig = (UInt32) (g*255+0.5), 257 ib = (UInt32) (b*255+0.5), 258 ia = (UInt32) (a*255+0.5); 259 260 // NSLog(@"%@ %d, %d, %d", c, ir, ig, ib); 261 262 return ((ia & 0xFF) << 24) | ((ir & 0xFF) << 16) | ((ig & 0xFF) << 8) | ((ib & 0xFF) << 0); 263 } 264 265 void doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) { 266 jint len = (*env)->GetArrayLength(env, jColors); 267 268 UInt32 colorsArray[len]; 269 UInt32 *colors = colorsArray; 270 271 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 272 NSUInteger i; 273 for (i = 0; i < len; i++) { 274 colors[i] = RGB([CSystemColors getColor:i useAppleColor:useAppleColors]); 275 } 276 }]; 277 278 jint *_colors = (*env)->GetPrimitiveArrayCritical(env, jColors, 0); 279 memcpy(_colors, colors, len * sizeof(UInt32)); 280 (*env)->ReleasePrimitiveArrayCritical(env, jColors, _colors, 0); 281 } 282 283 /** 284 * Class: sun_lwawt_macosx_LWCToolkit 285 * Method: loadNativeColors 286 * Signature: ([I[I)V 287 */ 288 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_loadNativeColors 289 (JNIEnv *env, jobject peer, jintArray jSystemColors, jintArray jAppleColors) 290 { 291 JNF_COCOA_ENTER(env); 292 doLoadNativeColors(env, jSystemColors, NO); 293 doLoadNativeColors(env, jAppleColors, YES); 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: doAWTRunLoop 323 * Signature: (JZZ)V 324 */ 325 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoop 326 (JNIEnv *env, jclass clz, jlong mediator, jboolean awtMode, jboolean detectDeadlocks) 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 if (!sInPerformFromJava || !detectDeadlocks) { 336 337 NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; 338 NSDate *distantFuture = [NSDate distantFuture]; 339 NSString *mode = (awtMode) ? [JNFRunLoop javaRunLoopMode] : NSDefaultRunLoopMode; 340 341 BOOL isRunning = YES; 342 while (isRunning && ![mediatorObject shouldEndRunLoop]) { 343 // Don't use acceptInputForMode because that doesn't setup autorelease pools properly 344 isRunning = [currentRunLoop runMode:mode beforeDate:distantFuture]; 345 } 346 347 } 348 #ifndef PRODUCT_BUILD 349 if (sInPerformFromJava) { 350 NSLog(@"Apple AWT: Short-circuiting CToolkit.invokeAndWait trampoline deadlock!!!!!"); 351 NSLog(@"\tPlease file a bug report with this message and a reproducible test case."); 352 } 353 #endif 354 355 CFRelease(mediatorObject); 356 357 JNF_COCOA_EXIT(env); 358 } 359 360 /* 361 * Class: sun_lwawt_macosx_LWCToolkit 362 * Method: stopAWTRunLoop 363 * Signature: (J)V 364 */ 365 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopAWTRunLoop 366 (JNIEnv *env, jclass clz, jlong mediator) 367 { 368 AWT_ASSERT_NOT_APPKIT_THREAD; 369 JNF_COCOA_ENTER(env); 370 371 AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator); 372 373 [ThreadUtilities performOnMainThread:@selector(endRunLoop) onObject:mediatorObject withObject:nil waitUntilDone:NO awtMode:YES]; 374 375 CFRelease(mediatorObject); 376 377 JNF_COCOA_EXIT(env); 378 } 379 380 /* 381 * Class: sun_lwawt_macosx_LWCToolkit 382 * Method: isCapsLockOn 383 * Signature: ()Z 384 */ 385 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isCapsLockOn 386 (JNIEnv *env, jobject self) 387 { 388 __block jboolean isOn = JNI_FALSE; 389 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 390 NSUInteger modifiers = [NSEvent modifierFlags]; 391 isOn = (modifiers & NSAlphaShiftKeyMask) != 0; 392 }]; 393 394 return isOn; 395 } 396 397 /* 398 * Class: sun_lwawt_macosx_LWCToolkit 399 * Method: isApplicationActive 400 * Signature: ()Z 401 */ 402 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive 403 (JNIEnv *env, jclass clazz) 404 { 405 __block jboolean active = JNI_FALSE; 406 407 JNF_COCOA_ENTER(env); 408 409 if ([NSThread isMainThread]) { 410 active = (jboolean)[NSRunningApplication currentApplication].active; 411 } else { 412 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^() { 413 active = (jboolean)[NSRunningApplication currentApplication].active; 414 }]; 415 } 416 417 JNF_COCOA_EXIT(env); 418 419 return active; 420 } 421 422 423 /* 424 * Class: sun_awt_SunToolkit 425 * Method: closeSplashScreen 426 * Signature: ()V 427 */ 428 JNIEXPORT void JNICALL 429 Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls) 430 { 431 void *hSplashLib = dlopen(0, RTLD_LAZY); 432 if (!hSplashLib) return; 433 434 void (*splashClose)() = dlsym(hSplashLib, "SplashClose"); 435 if (splashClose) { 436 splashClose(); 437 } 438 dlclose(hSplashLib); 439 } 440 441 442 // TODO: definitely doesn't belong here (copied from fontpath.c in the 443 // solaris tree)... 444 445 JNIEXPORT jstring JNICALL 446 Java_sun_font_FontManager_getFontPath 447 (JNIEnv *env, jclass obj, jboolean noType1) 448 { 449 return JNFNSToJavaString(env, @"/Library/Fonts"); 450 } 451 452 // This isn't yet used on unix, the implementation is added since shared 453 // code calls this method in preparation for future use. 454 JNIEXPORT void JNICALL 455 Java_sun_font_FontManager_populateFontFileNameMap 456 (JNIEnv *env, jclass obj, jobject fontToFileMap, jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale) 457 { 458 459 } 460 461 /* 462 * Class: sun_lwawt_macosx_LWCToolkit 463 * Method: startNativeNestedEventLoop 464 * Signature: ()V 465 */ 466 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_startNativeNestedEventLoop 467 (JNIEnv *env, jclass cls) 468 { 469 if(!shouldKeepRunningNestedLoop) { 470 NSRunLoop *theRL = [NSRunLoop currentRunLoop]; 471 NSApplication * app = [NSApplication sharedApplication]; 472 shouldKeepRunningNestedLoop = YES; 473 while (shouldKeepRunningNestedLoop && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) 474 { 475 NSEvent * event = [app nextEventMatchingMask: 0xFFFFFFFF untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; 476 if (event != nil) { 477 [app sendEvent: event]; 478 } 479 } 480 } 481 } 482 483 /* 484 * Class: sun_lwawt_macosx_LWCToolkit 485 * Method: stopNativeNestedEventLoop 486 * Signature: ()V 487 */ 488 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_stopNativeNestedEventLoop 489 (JNIEnv *env, jclass cls) 490 { 491 shouldKeepRunningNestedLoop = NO; 492 }