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