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