1 /* 2 * Copyright (c) 2011, 2017, 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 "common.h" 27 #import "com_sun_glass_ui_mac_MacApplication.h" 28 #import "com_sun_glass_events_KeyEvent.h" 29 30 31 #import "GlassMacros.h" 32 #import "GlassApplication.h" 33 #import "GlassHelper.h" 34 #import "GlassKey.h" 35 #import "GlassScreen.h" 36 #import "GlassWindow.h" 37 #import "GlassTouches.h" 38 #import "RemoteLayerSupport.h" 39 40 #import "ProcessInfo.h" 41 #import <Security/SecRequirement.h> 42 43 //#define VERBOSE 44 #ifndef VERBOSE 45 #define LOG(MSG, ...) 46 #else 47 #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__); 48 #endif 49 50 //#define VERBOSE_LOAD 51 52 static BOOL shouldKeepRunningNestedLoop = YES; 53 static jobject nestedLoopReturnValue = NULL; 54 static BOOL isFullScreenExitingLoop = NO; 55 static NSMutableDictionary * keyCodeForCharMap = nil; 56 static BOOL isEmbedded = NO; 57 static BOOL disableSyncRendering = NO; 58 59 jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) 60 { 61 pthread_key_create(&GlassThreadDataKey, NULL); 62 63 memset(&javaIDs, 0, sizeof(javaIDs)); 64 MAIN_JVM = vm; 65 return JNI_VERSION_1_4; 66 } 67 68 #pragma mark --- GlassRunnable 69 70 @interface GlassRunnable : NSObject 71 { 72 jobject jRunnable; 73 } 74 75 - (id)initWithRunnable:(jobject)runnable; 76 - (void)run; 77 78 @end 79 80 @implementation GlassRunnable 81 82 - (id)initWithRunnable:(jobject)runnable 83 { 84 self->jRunnable = runnable; 85 return self; 86 } 87 88 - (void)run 89 { 90 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 91 { 92 assert(pthread_main_np() == 1); 93 JNIEnv *env = jEnv; 94 if (env != NULL) 95 { 96 (*env)->CallVoidMethod(env, self->jRunnable, jRunnableRun); 97 GLASS_CHECK_EXCEPTION(env); 98 } 99 100 [self release]; 101 } 102 [pool drain]; 103 } 104 105 - (void)dealloc 106 { 107 assert(pthread_main_np() == 1); 108 JNIEnv *env = jEnv; 109 if (env != NULL) 110 { 111 (*env)->DeleteGlobalRef(env, self->jRunnable); 112 } 113 self->jRunnable = NULL; 114 115 [super dealloc]; 116 } 117 118 @end 119 120 #pragma mark --- GlassApplication 121 122 @implementation GlassApplication 123 124 - (id)initWithEnv:(JNIEnv*)env application:(jobject)application launchable:(jobject)launchable taskbarApplication:(jboolean)isTaskbarApplication classLoader:(jobject)classLoader 125 { 126 self = [super init]; 127 if (self != nil) 128 { 129 self->started = NO; 130 self->jTaskBarApp = isTaskbarApplication; 131 132 self->jApplication = (*env)->NewGlobalRef(env, application); 133 if (launchable != NULL) 134 { 135 self->jLaunchable = (*env)->NewGlobalRef(env, launchable); 136 } 137 138 if (classLoader != NULL) 139 { 140 [GlassHelper SetGlassClassLoader:classLoader withEnv:env]; 141 } 142 } 143 return self; 144 } 145 146 #pragma mark --- delegate methods 147 148 - (void)GlassApplicationDidChangeScreenParameters 149 { 150 LOG("GlassApplicationDidChangeScreenParameters"); 151 152 assert(pthread_main_np() == 1); 153 JNIEnv *env = jEnv; 154 if (env != NULL) 155 { 156 GlassScreenDidChangeScreenParameters(env); 157 } 158 } 159 160 - (void)applicationWillFinishLaunching:(NSNotification *)aNotification 161 { 162 LOG("GlassApplication:applicationWillFinishLaunching"); 163 164 GET_MAIN_JENV; 165 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 166 { 167 if (self->jLaunchable != NULL) 168 { 169 jclass runnableClass = [GlassHelper ClassForName:"java.lang.Runnable" withEnv:jEnv]; 170 if ((*env)->ExceptionCheck(env) == JNI_TRUE) 171 { 172 (*env)->ExceptionDescribe(env); 173 (*env)->ExceptionClear(env); 174 } 175 if (runnableClass) { 176 jmethodID runMethod = (*env)->GetMethodID(env, runnableClass, "run", "()V"); 177 if ((*env)->ExceptionCheck(env) == JNI_TRUE) 178 { 179 (*env)->ExceptionDescribe(env); 180 (*env)->ExceptionClear(env); 181 } 182 if (runMethod) { 183 (*env)->CallVoidMethod(env, self->jLaunchable, runMethod); 184 if ((*env)->ExceptionCheck(env) == JNI_TRUE) 185 { 186 (*env)->ExceptionDescribe(env); 187 (*env)->ExceptionClear(env); 188 } 189 else 190 { 191 [[NSNotificationCenter defaultCenter] addObserver:self 192 selector:@selector(GlassApplicationDidChangeScreenParameters) 193 name:NSApplicationDidChangeScreenParametersNotification 194 object:nil]; 195 196 // localMonitor = [NSEvent addLocalMonitorForEventsMatchingMask: NSRightMouseDownMask 197 // handler:^(NSEvent *incomingEvent) { 198 // NSEvent *result = incomingEvent; 199 // NSWindow *targetWindowForEvent = [incomingEvent window]; 200 // LOG("NSRightMouseDownMask local"); 201 // return result; 202 // }]; 203 // 204 // globalMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: NSRightMouseDownMask 205 // handler:^(NSEvent *incomingEvent) { 206 // NSEvent *result = incomingEvent; 207 // NSWindow *targetWindowForEvent = [incomingEvent window]; 208 // NSWindow *window = [[NSApplication sharedApplication] 209 // windowWithWindowNumber:[incomingEvent windowNumber]]; 210 // NSWindow *appWindow = [[NSApplication sharedApplication] mainWindow]; 211 // LOG("NSRightMouseDownMask global: %p num %d win %p appwin %p", 212 // targetWindowForEvent, [incomingEvent windowNumber], window, 213 // [[NSApplication sharedApplication] mainWindow]); 214 // }]; 215 } 216 } else { 217 NSLog(@"ERROR: Glass could not find run() method\n"); 218 } 219 } else { 220 NSLog(@"ERROR: Glass could not find Runnable class\n"); 221 } 222 } 223 224 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillFinishLaunchingMethod]); 225 226 self->started = YES; 227 } 228 [pool drain]; 229 GLASS_CHECK_EXCEPTION(env); 230 } 231 232 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 233 { 234 LOG("GlassApplication:applicationDidFinishLaunching"); 235 236 GET_MAIN_JENV; 237 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 238 { 239 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidFinishLaunchingMethod]); 240 } 241 [pool drain]; 242 GLASS_CHECK_EXCEPTION(env); 243 } 244 245 - (void)applicationWillBecomeActive:(NSNotification *)aNotification 246 { 247 LOG("GlassApplication:applicationWillBecomeActive"); 248 249 GET_MAIN_JENV; 250 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 251 { 252 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillBecomeActiveMethod]); 253 } 254 [pool drain]; 255 GLASS_CHECK_EXCEPTION(env); 256 } 257 258 - (void)applicationDidBecomeActive:(NSNotification *)aNotification 259 { 260 LOG("GlassApplication:applicationDidBecomeActive"); 261 262 GET_MAIN_JENV; 263 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 264 { 265 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidBecomeActiveMethod]); 266 } 267 [pool drain]; 268 GLASS_CHECK_EXCEPTION(env); 269 } 270 271 - (void)applicationWillResignActive:(NSNotification *)aNotification 272 { 273 LOG("GlassApplication:applicationWillResignActive"); 274 275 GET_MAIN_JENV; 276 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 277 { 278 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillResignActiveMethod]); 279 } 280 [pool drain]; 281 GLASS_CHECK_EXCEPTION(env); 282 } 283 284 - (void)applicationDidResignActive:(NSNotification *)aNotification 285 { 286 LOG("GlassApplication:applicationDidResignActive"); 287 288 GET_MAIN_JENV; 289 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 290 { 291 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidResignActiveMethod]); 292 } 293 [pool drain]; 294 GLASS_CHECK_EXCEPTION(env); 295 } 296 297 - (void)applicationWillHide:(NSNotification *)aNotification 298 { 299 LOG("GlassApplication:applicationWillHide"); 300 301 GET_MAIN_JENV; 302 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 303 { 304 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillHideMethod]); 305 } 306 [pool drain]; 307 GLASS_CHECK_EXCEPTION(env); 308 } 309 310 - (void)applicationDidHide:(NSNotification *)aNotification 311 { 312 LOG("GlassApplication:applicationDidHide"); 313 314 GET_MAIN_JENV; 315 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 316 { 317 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidHideMethod]); 318 } 319 [pool drain]; 320 GLASS_CHECK_EXCEPTION(env); 321 } 322 323 - (void)applicationWillUnhide:(NSNotification *)aNotification 324 { 325 LOG("GlassApplication:applicationWillUnhide"); 326 327 GET_MAIN_JENV; 328 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 329 { 330 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillUnhideMethod]); 331 } 332 [pool drain]; 333 GLASS_CHECK_EXCEPTION(env); 334 } 335 336 - (void)applicationDidUnhide:(NSNotification *)aNotification 337 { 338 LOG("GlassApplication:applicationDidUnhide"); 339 340 GET_MAIN_JENV; 341 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 342 { 343 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyDidUnhideMethod]); 344 } 345 [pool drain]; 346 GLASS_CHECK_EXCEPTION(env); 347 } 348 349 - (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames 350 { 351 LOG("GlassApplication:application:openFiles"); 352 353 GET_MAIN_JENV; 354 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 355 { 356 NSUInteger count = [filenames count]; 357 jclass stringClass = [GlassHelper ClassForName:"java.lang.String" withEnv:env]; 358 if (!stringClass) { 359 return; 360 } 361 jobjectArray files = (*env)->NewObjectArray(env, (jsize)count, stringClass, NULL); 362 GLASS_CHECK_EXCEPTION(env); 363 for (NSUInteger i=0; i<count; i++) 364 { 365 NSString *file = [filenames objectAtIndex:i]; 366 if (file != nil) 367 { 368 (*env)->SetObjectArrayElement(env, files, (jsize)i, (*env)->NewStringUTF(env, [file UTF8String])); 369 GLASS_CHECK_EXCEPTION(env); 370 } 371 } 372 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyOpenFilesMethod], files); 373 } 374 [pool drain]; 375 GLASS_CHECK_EXCEPTION(env); 376 377 [theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; 378 } 379 380 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename 381 { 382 LOG("GlassApplication:application:openFile"); 383 384 // controlled by Info.plist -NSOpenfileName 385 // http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigApplications.html 386 [self application:theApplication openFiles:[NSArray arrayWithObject:filename]]; 387 388 return YES; 389 } 390 391 - (BOOL)application:(id)theApplication openFileWithoutUI:(NSString *)filename 392 { 393 LOG("GlassApplication:application:openFileWithoutUI"); 394 395 // programmaticaly called by the client (even though GlassApplication does not currently call it, let's wire it in just in case) 396 [self application:theApplication openFiles:[NSArray arrayWithObject:filename]]; 397 398 return YES; 399 } 400 401 - (BOOL)application:(NSApplication *)theApplication openTempFile:(NSString *)filename 402 { 403 LOG("GlassApplication:application:openTempFile"); 404 405 // controlled by Info.plist -NSOpenTempfileName 406 // http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigApplications.html 407 // NOP 408 409 return YES; 410 } 411 412 - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender 413 { 414 LOG("GlassApplication:applicationShouldOpenUntitledFile"); 415 416 // don't want 417 418 return NO; 419 } 420 421 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender 422 { 423 LOG("GlassApplication:applicationShouldTerminate"); 424 425 GET_MAIN_JENV; 426 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 427 { 428 (*env)->CallVoidMethod(env, self->jApplication, [GlassHelper ApplicationNotifyWillQuitMethod]); 429 } 430 [pool drain]; 431 GLASS_CHECK_EXCEPTION(env); 432 433 return NSTerminateCancel; 434 } 435 436 437 - (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication 438 { 439 LOG("GlassApplication:applicationOpenUntitledFile"); 440 441 // NOP (should never be called because applicationShouldOpenUntitledFile returns NO) 442 443 return YES; 444 } 445 446 #pragma mark --- Glass support 447 448 - (void)runLoop:(id)selector 449 { 450 LOG("GlassApplication:runLoop ENTER"); 451 452 NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init]; 453 454 jint error = (*jVM)->AttachCurrentThread(jVM, (void **)&jEnv, NULL); 455 //jint error = (*jVM)->AttachCurrentThreadAsDaemon(jVM, (void **)&jEnv, NULL); 456 if (error == 0) 457 { 458 NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init]; 459 460 if ([[NSThread currentThread] name] == nil) 461 { 462 [[NSThread currentThread] setName:@"Main Cocoa (UI) Thread"]; 463 } 464 465 GlassApplication *glassApp = (GlassApplication *)selector; 466 467 // Load MacApplication class using the glass classloader 468 jclass cls = [GlassHelper ClassForName:"com.sun.glass.ui.mac.MacApplication" withEnv:jEnv]; 469 if (!cls) 470 { 471 NSLog(@"ERROR: can't find the MacApplication class"); 472 } 473 else 474 { 475 jmethodID setEventThreadMID = (*jEnv)->GetMethodID(jEnv, cls, "setEventThread", "()V"); 476 if (!setEventThreadMID) 477 { 478 NSLog(@"ERROR: can't get MacApplication.setEventThread() method ID"); 479 } 480 else 481 { 482 (*jEnv)->CallVoidMethod(jEnv, glassApp->jApplication, setEventThreadMID); 483 } 484 } 485 GLASS_CHECK_EXCEPTION(jEnv); 486 487 NSBundle *mainBundle = [NSBundle mainBundle]; 488 { 489 NSString *appName = [mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]; 490 491 if (appName == nil) { 492 appName = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"]; 493 } 494 495 if (appName) { 496 // make the name available to Java side, before Launchable.fnishLaunching callback 497 jstring jname = (*jEnv)->NewStringUTF(jEnv, [appName UTF8String]); 498 jmethodID setNameMethod = (*jEnv)->GetMethodID(jEnv, cls, "setName", "(Ljava/lang/String;)V"); 499 GLASS_CHECK_EXCEPTION(jEnv); 500 if (setNameMethod != NULL) { 501 (*jEnv)->CallVoidMethod(jEnv, glassApp->jApplication, setNameMethod, jname); 502 } 503 GLASS_CHECK_EXCEPTION(jEnv); 504 } 505 } 506 507 // Determine if we're running embedded (in AWT, SWT, elsewhere) 508 NSApplication *app = [NSApplication sharedApplication]; 509 isEmbedded = [app isRunning]; 510 511 if (!isEmbedded) 512 { 513 if (self->jTaskBarApp == JNI_TRUE) 514 { 515 // move process from background only to full on app with visible Dock icon 516 ProcessSerialNumber psn; 517 if (GetCurrentProcess(&psn) == noErr) 518 { 519 TransformProcessType(&psn, kProcessTransformToForegroundApplication); 520 } 521 522 NSString *CFBundleIconFile = [mainBundle objectForInfoDictionaryKey:@"CFBundleIconFile"]; 523 NSString *iconPath = nil; 524 if (CFBundleIconFile != nil) 525 { 526 iconPath = [mainBundle pathForResource:[CFBundleIconFile stringByDeletingPathExtension] ofType:[CFBundleIconFile pathExtension]]; 527 } 528 529 // -Xdock:icon can override CFBundleIconFile (but only if it actually points to a valid icon) 530 NSString *property = [NSString stringWithFormat:@"APP_ICON_%d", [[NSProcessInfo processInfo] processIdentifier]]; 531 char *path = getenv([property UTF8String]); 532 if (path != NULL) 533 { 534 NSString *overridenPath = [NSString stringWithFormat:@"%s", path]; 535 if ([[NSFileManager defaultManager] fileExistsAtPath:overridenPath isDirectory:NO] == YES) 536 { 537 iconPath = overridenPath; 538 } 539 } 540 if ([[NSFileManager defaultManager] fileExistsAtPath:iconPath isDirectory:NO] == NO) 541 { 542 // try again using Java generic icon (this icon might go away eventually ?) 543 iconPath = [NSString stringWithFormat:@"%s", "/System/Library/Frameworks/JavaVM.framework/Resources/GenericApp.icns"]; 544 } 545 546 NSImage *image = nil; 547 { 548 if ([[NSFileManager defaultManager] fileExistsAtPath:iconPath isDirectory:NO] == YES) 549 { 550 image = [[NSImage alloc] initWithContentsOfFile:iconPath]; 551 } 552 if (image == nil) 553 { 554 // last resort - if still no icon, then ask for an empty standard app icon, which is guranteed to exist 555 image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; 556 } 557 } 558 [app setApplicationIconImage:image]; 559 [image release]; 560 561 // Install a hidden Window menu. This allows the dock icon 562 // menu to show the list of open windows (NSWindow instances) 563 NSMenu *myMenu = [[NSMenu alloc] initWithTitle:@"Window"]; 564 [app setWindowsMenu:myMenu]; 565 [myMenu release]; 566 567 [app setDelegate:self]; 568 569 // [app activateIgnoringOtherApps:YES] won't activate the menu bar on OS X 10.9, so instead we do this: 570 [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateIgnoringOtherApps | NSApplicationActivateAllWindows)]; 571 } 572 else 573 { 574 // allow background processes to change the cursor (10.8 only API so we'll have to dynamically call it if available) 575 { 576 BOOL yes = YES; 577 [GlassHelper InvokeSelectorIfAvailable:@selector(javaSetAllowsCursorSetInBackground:) forClass:[NSCursor class] withArgument:&yes withReturnValue:NULL]; 578 } 579 580 // http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/cross_development/Using/using.html 581 if (floor(NSAppKitVersionNumber) >= 1138) // NSAppKitVersionNumber10_7 582 { 583 // 10.7 or later: move process from background only process to a limited app with active windows, 584 // but no Dock icon 585 ProcessSerialNumber psn; 586 if (GetCurrentProcess(&psn) == noErr) 587 { 588 TransformProcessType(&psn, 4); // kProcessTransformToUIElementApplication 589 } 590 } 591 else 592 { 593 // 10.6 or earlier: applets are not officially supported on 10.6 and earlier 594 // so they will have limited applet functionality (no active windows) 595 } 596 [app setDelegate:self]; 597 } 598 599 #if defined(VERBOSE_LOAD) 600 jclass BooleanClass = [GlassHelper ClassForName:"java.lang.Boolean" withEnv:jEnv]; 601 if (BooleanClass != 0) 602 { 603 jmethodID getBooleanMethod = (*jEnv)->GetStaticMethodID(jEnv, BooleanClass, "getBoolean", "(Ljava/lang/String;)Z"); 604 if (getBooleanMethod != 0) 605 { 606 jstring flag = (*jEnv)->NewStringUTF(jEnv, "glassload.verbose"); 607 jboolean verbose = (*jEnv)->CallStaticBooleanMethod(jEnv, BooleanClass, getBooleanMethod, flag); 608 if (verbose == JNI_TRUE) 609 { 610 printLoadedLibraries(stderr); 611 printLoadedFiles(stderr); 612 } 613 } 614 } 615 #endif 616 617 // drain the pool before entering runloop 618 [pool2 drain]; 619 620 // enter runloop, this will not return until terminated 621 [NSApp run]; 622 623 // Abort listerning to global touch input events 624 [GlassTouches terminate]; 625 626 GLASS_CHECK_EXCEPTION(jEnv); 627 628 (*jEnv)->CallVoidMethod(jEnv, self->jApplication, javaIDs.MacApplication.notifyApplicationDidTerminate); 629 GLASS_CHECK_EXCEPTION(jEnv); 630 631 jint err = (*jVM)->DetachCurrentThread(jVM); 632 if (err < 0) 633 { 634 NSLog(@"Unable to detach from JVM. Error code: %d\n", (int)err); 635 } 636 637 jEnv = NULL; 638 } 639 else // event loop is not started 640 { 641 if ([NSThread isMainThread] == YES) { 642 [glassApp applicationWillFinishLaunching: NULL]; 643 } else { 644 [glassApp performSelectorOnMainThread:@selector(applicationWillFinishLaunching:) withObject:NULL waitUntilDone:NO]; 645 } 646 GLASS_CHECK_EXCEPTION(jEnv); 647 648 [pool2 drain]; 649 } 650 } 651 else // attaching to JVM failed 652 { 653 NSLog(@"ERROR: Glass could not attach to VM, result:%d\n", (int)error); 654 } 655 656 [pool1 drain]; 657 658 LOG("GlassApplication:runLoop EXIT"); 659 } 660 661 - (BOOL)started 662 { 663 return self->started; 664 } 665 666 + (jobject)enterNestedEventLoopWithEnv:(JNIEnv*)env 667 { 668 jobject ret = NULL; 669 670 NSRunLoop *theRL = [NSRunLoop currentRunLoop]; 671 NSApplication * app = [NSApplication sharedApplication]; 672 shouldKeepRunningNestedLoop = YES; 673 // Cannot use [NSDate distantFuture] because the period is big the app could hang in a runloop 674 // if the event came before entering the RL 675 while (shouldKeepRunningNestedLoop && [theRL runMode:NSDefaultRunLoopMode 676 beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]]) 677 { 678 NSEvent * event = [app nextEventMatchingMask: 0xFFFFFFFF untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES]; 679 680 if (event != nil) { 681 [app sendEvent: event]; 682 } 683 } 684 685 if (nestedLoopReturnValue != NULL) { 686 ret = (*env)->NewLocalRef(env, nestedLoopReturnValue); 687 (*env)->DeleteGlobalRef(env, nestedLoopReturnValue); 688 nestedLoopReturnValue = NULL; 689 } 690 691 shouldKeepRunningNestedLoop = YES; 692 693 return ret; 694 } 695 696 + (void)leaveNestedEventLoopWithEnv:(JNIEnv*)env retValue:(jobject)retValue 697 { 698 if (retValue != NULL) { 699 nestedLoopReturnValue = (*env)->NewGlobalRef(env, retValue); 700 } 701 shouldKeepRunningNestedLoop = NO; 702 } 703 704 + (void)enterFullScreenExitingLoop 705 { 706 if (isFullScreenExitingLoop) { 707 return; 708 } 709 isFullScreenExitingLoop = YES; 710 GET_MAIN_JENV; 711 (*env)->CallStaticObjectMethod(env, jApplicationClass, 712 javaIDs.Application.enterNestedEventLoop); 713 if ((*env)->ExceptionCheck(env) == JNI_TRUE) { 714 (*env)->ExceptionDescribe(env); 715 (*env)->ExceptionClear(env); 716 } 717 isFullScreenExitingLoop = NO; 718 } 719 720 + (void)leaveFullScreenExitingLoopIfNeeded 721 { 722 if (!isFullScreenExitingLoop) { 723 return; 724 } 725 GET_MAIN_JENV; 726 (*env)->CallStaticVoidMethod(env, jApplicationClass, 727 javaIDs.Application.leaveNestedEventLoop, (jobject)NULL); 728 } 729 730 + (void)registerKeyEvent:(NSEvent*)event 731 { 732 if (!keyCodeForCharMap) { 733 keyCodeForCharMap = [[NSMutableDictionary alloc] initWithCapacity:100]; 734 // Note: it's never released, just like, say, the jApplication reference... 735 } 736 [keyCodeForCharMap setObject:[NSNumber numberWithUnsignedShort:[event keyCode]] forKey:[event characters]]; 737 } 738 739 + (jint)getKeyCodeForChar:(jchar)c; 740 { 741 id v = [keyCodeForCharMap objectForKey:[NSString stringWithCharacters: (UniChar *)&c length:1]]; 742 if (!v) { 743 return com_sun_glass_events_KeyEvent_VK_UNDEFINED; 744 } else { 745 return GetJavaKeyCodeFor([v unsignedShortValue]); 746 } 747 } 748 749 + (BOOL)syncRenderingDisabled { 750 return disableSyncRendering; 751 } 752 753 + (BOOL)isSandboxed 754 { 755 static int isSandboxed = -1; 756 757 if (isSandboxed == -1) { 758 isSandboxed = 0; 759 760 NSBundle *mainBundle = [NSBundle mainBundle]; 761 NSURL *url = [mainBundle bundleURL]; 762 SecStaticCodeRef staticCodeRef = NULL; 763 SecStaticCodeCreateWithPath((CFURLRef)url, kSecCSDefaultFlags, &staticCodeRef); 764 765 if (staticCodeRef) { 766 // Check if the app is signed 767 OSStatus res_signed = SecStaticCodeCheckValidityWithErrors(staticCodeRef, kSecCSBasicValidateOnly, NULL, NULL); 768 if (res_signed == errSecSuccess) { 769 // It is signed, now check if it's sandboxed 770 SecRequirementRef sandboxRequirementRef = NULL; 771 SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"), kSecCSDefaultFlags, &sandboxRequirementRef); 772 773 if (sandboxRequirementRef) { 774 OSStatus res_sandboxed = SecStaticCodeCheckValidityWithErrors(staticCodeRef, kSecCSBasicValidateOnly, sandboxRequirementRef, NULL); 775 if (res_sandboxed == errSecSuccess) { 776 // Yep, sandboxed 777 isSandboxed = 1; 778 } 779 780 CFRelease(sandboxRequirementRef); 781 } 782 } 783 784 CFRelease(staticCodeRef); 785 } 786 } 787 788 return isSandboxed == 1 ? YES : NO; 789 } 790 791 @end 792 793 #pragma mark --- JNI 794 795 /* 796 * Class: com_sun_glass_ui_mac_MacApplication 797 * Method: _initIDs 798 * Signature: ()V 799 */ 800 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1initIDs 801 (JNIEnv *env, jclass jClass, jboolean jDisableSyncRendering) 802 { 803 LOG("Java_com_sun_glass_ui_mac_MacApplication__1initIDs"); 804 805 disableSyncRendering = jDisableSyncRendering ? YES : NO; 806 807 jApplicationClass = (*env)->NewGlobalRef(env, jClass); 808 809 javaIDs.Application.createPixels = (*env)->GetStaticMethodID( 810 env, jClass, "createPixels", "(II[IFF)Lcom/sun/glass/ui/Pixels;"); 811 if ((*env)->ExceptionCheck(env)) return; 812 813 javaIDs.Application.getScaleFactor = (*env)->GetStaticMethodID( 814 env, jClass, "getScaleFactor", "(IIII)F"); 815 if ((*env)->ExceptionCheck(env)) return; 816 817 javaIDs.Application.reportException = (*env)->GetStaticMethodID( 818 env, jClass, "reportException", "(Ljava/lang/Throwable;)V"); 819 if ((*env)->ExceptionCheck(env)) return; 820 821 javaIDs.Application.enterNestedEventLoop = (*env)->GetStaticMethodID( 822 env, jClass, "enterNestedEventLoop", "()Ljava/lang/Object;"); 823 if ((*env)->ExceptionCheck(env)) return; 824 825 javaIDs.Application.leaveNestedEventLoop = (*env)->GetStaticMethodID( 826 env, jClass, "leaveNestedEventLoop", "(Ljava/lang/Object;)V"); 827 if ((*env)->ExceptionCheck(env)) return; 828 829 javaIDs.MacApplication.notifyApplicationDidTerminate = (*env)->GetMethodID( 830 env, jClass, "notifyApplicationDidTerminate", "()V"); 831 if ((*env)->ExceptionCheck(env)) return; 832 833 if (jRunnableRun == NULL) 834 { 835 jclass jcls = (*env)->FindClass(env, "java/lang/Runnable"); 836 if ((*env)->ExceptionCheck(env)) return; 837 jRunnableRun = (*env)->GetMethodID(env, jcls, "run", "()V"); 838 if ((*env)->ExceptionCheck(env)) return; 839 } 840 } 841 842 /* 843 * Class: com_sun_glass_ui_mac_MacApplication 844 * Method: _runLoop 845 * Signature: (Ljava/lang/ClassLoader;Ljava/lang/Runnable;Z)V 846 */ 847 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1runLoop 848 (JNIEnv *env, jobject japplication, jobject classLoader, 849 jobject jlaunchable, jboolean isTaskbarApplication) 850 { 851 LOG("Java_com_sun_glass_ui_mac_MacApplication__1runLoop"); 852 853 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 854 { 855 if ([NSThread isMainThread] == YES) 856 { 857 // fprintf(stderr, "\nWARNING: Glass was started on 1st thread and will block this thread.\nYou most likely do not want to do this - please remove \"-XstartOnFirstThread\" from VM arguments.\n\n"); 858 } 859 else 860 { 861 if ([[NSThread currentThread] name] == nil) 862 { 863 [[NSThread currentThread] setName:@"Main Java Thread"]; 864 } 865 } 866 867 GlassApplication *glass = [[GlassApplication alloc] initWithEnv:env application:japplication launchable:jlaunchable taskbarApplication:isTaskbarApplication classLoader:classLoader]; 868 if ([NSThread isMainThread] == YES) { 869 [glass runLoop: glass]; 870 } else { 871 [glass performSelectorOnMainThread:@selector(runLoop:) withObject:glass waitUntilDone:[NSThread isMainThread]]; 872 873 // wait for Cocoa to enter its UI runloop 874 while ([glass started] == NO) 875 { 876 LOG(" waiting for [glass started]"); 877 usleep(10000); 878 } 879 } 880 881 // at this point Java main thread is allowed to proceed, but Cocoa's UI thread entered its runloop, so the VM will not quit 882 } 883 [glasspool drain]; glasspool=nil; 884 GLASS_CHECK_EXCEPTION(env); 885 } 886 887 /* 888 * Class: com_sun_glass_ui_mac_MacApplication 889 * Method: _finishTerminating 890 * Signature: ()V 891 */ 892 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1finishTerminating 893 (JNIEnv *env, jobject japplication) 894 { 895 LOG("Java_com_sun_glass_ui_mac_MacApplication__1finishTerminating"); 896 897 if (isEmbedded) { 898 return; 899 } 900 901 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 902 { 903 [NSApp stop:nil]; 904 [NSApp hide:nil]; 905 906 // wake up the runloop one last time so that it can process the stop: 907 // request, even if the app is inactive currently 908 NSTimeInterval dummyEventTimestamp = [NSProcessInfo processInfo].systemUptime; 909 NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined 910 location: NSMakePoint(0,0) 911 modifierFlags: 0 912 timestamp: dummyEventTimestamp 913 windowNumber: 0 914 context: nil 915 subtype: 0 916 data1: 0 917 data2: 0]; 918 [NSApp postEvent: event atStart: NO]; 919 } 920 [glasspool drain]; glasspool=nil; 921 GLASS_CHECK_EXCEPTION(env); 922 } 923 924 /* 925 * Class: com_sun_glass_ui_mac_MacApplication 926 * Method: _enterNestedEventLoopImpl 927 * Signature: ()Ljava/lang/Object; 928 */ 929 JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacApplication__1enterNestedEventLoopImpl 930 (JNIEnv *env, jobject japplication) 931 { 932 LOG("Java_com_sun_glass_ui_mac_MacApplication__1enterNestedEventLoopImpl"); 933 934 jobject ret; 935 936 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 937 { 938 ret = [GlassApplication enterNestedEventLoopWithEnv:env]; 939 } 940 [glasspool drain]; glasspool=nil; 941 GLASS_CHECK_EXCEPTION(env); 942 943 return ret; 944 } 945 946 /* 947 * Class: com_sun_glass_ui_mac_MacApplication 948 * Method: _leaveNestedEventLoopImpl 949 * Signature: (Ljava/lang/Object;)V 950 */ 951 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1leaveNestedEventLoopImpl 952 (JNIEnv *env, jobject japplication, jobject retValue) 953 { 954 LOG("Java_com_sun_glass_ui_mac_MacApplication__1leaveNestedEventLoopImpl"); 955 956 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 957 { 958 [GlassApplication leaveNestedEventLoopWithEnv:env retValue:retValue]; 959 } 960 [glasspool drain]; glasspool=nil; 961 GLASS_CHECK_EXCEPTION(env); 962 } 963 964 /* 965 * Class: com_sun_glass_ui_Application 966 * Method: _submitForLaterInvocation 967 * Signature: (Ljava/lang/Runnable;)V 968 */ 969 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1submitForLaterInvocation 970 (JNIEnv *env, jobject japplication, jobject jRunnable) 971 { 972 //LOG("Java_com_sun_glass_ui_mac_MacApplication_submitForLaterInvocation"); 973 974 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 975 if (jEnv != NULL) 976 { 977 GlassRunnable *runnable = [[GlassRunnable alloc] initWithRunnable:(*env)->NewGlobalRef(env, jRunnable)]; 978 [runnable performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO]; 979 } 980 } 981 982 /* 983 * Class: com_sun_glass_ui_Application 984 * Method: _invokeAndWait 985 * Signature: (Ljava/lang/Runnable;)V 986 */ 987 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1invokeAndWait 988 (JNIEnv *env, jobject japplication, jobject jRunnable) 989 { 990 LOG("Java_com_sun_glass_ui_mac_MacApplication__1invokeAndWait"); 991 992 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 993 if (jEnv != NULL) 994 { 995 GlassRunnable *runnable = [[GlassRunnable alloc] initWithRunnable:(*env)->NewGlobalRef(env, jRunnable)]; 996 [runnable performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 997 } 998 } 999 1000 /* 1001 * Class: com_sun_glass_ui_mac_MacApplication 1002 * Method: _getRemoteLayerServerName 1003 * Signature: ()Ljava/lang/String; 1004 */ 1005 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_mac_MacApplication__1getRemoteLayerServerName 1006 (JNIEnv *env, jobject japplication) 1007 { 1008 LOG("Java_com_sun_glass_ui_mac_MacPasteboard__1getName"); 1009 1010 jstring name = NULL; 1011 1012 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 1013 GLASS_POOL_ENTER; 1014 { 1015 static mach_port_t remoteLayerServerPort = MACH_PORT_NULL; 1016 if (remoteLayerServerPort == MACH_PORT_NULL) 1017 { 1018 remoteLayerServerPort = RemoteLayerStartServer(); 1019 } 1020 NSString *remoteLayerServerName = RemoteLayerGetServerName(remoteLayerServerPort); 1021 name = (*env)->NewStringUTF(env, [remoteLayerServerName UTF8String]); 1022 } 1023 GLASS_POOL_EXIT; 1024 GLASS_CHECK_EXCEPTION(env); 1025 1026 return name; 1027 } 1028 1029 /* 1030 * Class: com_sun_glass_ui_mac_MacApplication 1031 * Method: staticScreen_getVideoRefreshPeriod 1032 * Signature: ()D 1033 */ 1034 JNIEXPORT jdouble JNICALL 1035 Java_com_sun_glass_ui_mac_MacApplication_staticScreen_1getVideoRefreshPeriod 1036 (JNIEnv *env, jobject jApplication) 1037 { 1038 LOG("Java_com_sun_glass_ui_mac_MacApplication__1getVideoRefreshPeriod"); 1039 1040 if (GlassDisplayLink != NULL) 1041 { 1042 double outRefresh = CVDisplayLinkGetActualOutputVideoRefreshPeriod(GlassDisplayLink); 1043 LOG("CVDisplayLinkGetActualOutputVideoRefreshPeriod: %f", outRefresh); 1044 return (outRefresh * 1000.0); // to millis 1045 } 1046 else 1047 { 1048 return 0.0; 1049 } 1050 } 1051 1052 /* 1053 * Class: com_sun_glass_ui_mac_MacApplication 1054 * Method: staticScreen_getScreens 1055 * Signature: ()[Lcom/sun/glass/ui/Screen; 1056 */ 1057 JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_mac_MacApplication_staticScreen_1getScreens 1058 (JNIEnv *env, jobject jApplication) 1059 { 1060 LOG("Java_com_sun_glass_ui_mac_MacApplication__1getScreens"); 1061 1062 jobjectArray screenArray = nil; 1063 1064 GLASS_POOL_ENTER; 1065 { 1066 screenArray = createJavaScreens(env); 1067 } 1068 GLASS_POOL_EXIT; 1069 GLASS_CHECK_EXCEPTION(env); 1070 1071 return screenArray; 1072 } 1073 1074 1075 /* 1076 * Class: com_sun_glass_ui_mac_MacApplication 1077 * Method: _supportsSystemMenu 1078 * Signature: ()Z; 1079 */ 1080 JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacApplication__1supportsSystemMenu 1081 (JNIEnv *env, jobject japplication) 1082 { 1083 return !isEmbedded; 1084 } 1085 1086 /* 1087 * Class: com_sun_glass_ui_mac_MacApplication 1088 * Method: _hide 1089 * Signature: ()V; 1090 */ 1091 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1hide 1092 (JNIEnv *env, jobject japplication) 1093 { 1094 [NSApp hide:NSApp]; 1095 } 1096 1097 /* 1098 * Class: com_sun_glass_ui_mac_MacApplication 1099 * Method: _hideOtherApplications 1100 * Signature: ()V; 1101 */ 1102 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1hideOtherApplications 1103 (JNIEnv *env, jobject japplication) 1104 { 1105 [NSApp hideOtherApplications:NSApp]; 1106 } 1107 1108 /* 1109 * Class: com_sun_glass_ui_mac_MacApplication 1110 * Method: _unhideAllApplications 1111 * Signature: ()V; 1112 */ 1113 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1unhideAllApplications 1114 (JNIEnv *env, jobject japplication) 1115 { 1116 [NSApp unhideAllApplications:NSApp]; 1117 } 1118 1119 /* 1120 * Class: com_sun_glass_ui_mac_MacApplication 1121 * Method: _getDataDirectory 1122 * Signature: ()Ljava/lang/String; 1123 */ 1124 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_mac_MacApplication__1getDataDirectory 1125 (JNIEnv * env, jobject japplication) 1126 { 1127 jstring string = nil; 1128 1129 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 1130 GLASS_POOL_ENTER; 1131 { 1132 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 1133 if (paths && [paths count] > 0) { 1134 string = (*env)->NewStringUTF(jEnv, [[paths lastObject] UTF8String]); 1135 } 1136 } 1137 GLASS_POOL_EXIT; 1138 GLASS_CHECK_EXCEPTION(env); 1139 1140 return string; 1141 } 1142 1143 /* 1144 * Class: com_sun_glass_ui_mac_MacApplication 1145 * Method: _getMacKey 1146 * Signature: (I)I 1147 */ 1148 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacApplication__1getMacKey 1149 (JNIEnv *env, jclass jClass, jint code) 1150 { 1151 unsigned short macCode = 0; 1152 GetMacKey(code, &macCode); 1153 return (macCode & 0xFFFF); 1154 }