1 /* 2 * Copyright (c) 2011, 2016, 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 isFullScreenExitingLoop = YES; 708 GET_MAIN_JENV; 709 (*env)->CallStaticObjectMethod(env, jApplicationClass, 710 javaIDs.Application.enterNestedEventLoop); 711 if ((*env)->ExceptionCheck(env) == JNI_TRUE) { 712 (*env)->ExceptionDescribe(env); 713 (*env)->ExceptionClear(env); 714 } 715 isFullScreenExitingLoop = NO; 716 } 717 718 + (void)leaveFullScreenExitingLoopIfNeeded 719 { 720 if (!isFullScreenExitingLoop) { 721 return; 722 } 723 GET_MAIN_JENV; 724 (*env)->CallStaticVoidMethod(env, jApplicationClass, 725 javaIDs.Application.leaveNestedEventLoop, (jobject)NULL); 726 } 727 728 + (void)registerKeyEvent:(NSEvent*)event 729 { 730 if (!keyCodeForCharMap) { 731 keyCodeForCharMap = [[NSMutableDictionary alloc] initWithCapacity:100]; 732 // Note: it's never released, just like, say, the jApplication reference... 733 } 734 [keyCodeForCharMap setObject:[NSNumber numberWithUnsignedShort:[event keyCode]] forKey:[event characters]]; 735 } 736 737 + (jint)getKeyCodeForChar:(jchar)c; 738 { 739 id v = [keyCodeForCharMap objectForKey:[NSString stringWithCharacters: (UniChar *)&c length:1]]; 740 if (!v) { 741 return com_sun_glass_events_KeyEvent_VK_UNDEFINED; 742 } else { 743 return GetJavaKeyCodeFor([v unsignedShortValue]); 744 } 745 } 746 747 + (BOOL)syncRenderingDisabled { 748 return disableSyncRendering; 749 } 750 751 + (BOOL)isSandboxed 752 { 753 static int isSandboxed = -1; 754 755 if (isSandboxed == -1) { 756 isSandboxed = 0; 757 758 NSBundle *mainBundle = [NSBundle mainBundle]; 759 NSURL *url = [mainBundle bundleURL]; 760 SecStaticCodeRef staticCodeRef = NULL; 761 SecStaticCodeCreateWithPath((CFURLRef)url, kSecCSDefaultFlags, &staticCodeRef); 762 763 if (staticCodeRef) { 764 // Check if the app is signed 765 OSStatus res_signed = SecStaticCodeCheckValidityWithErrors(staticCodeRef, kSecCSBasicValidateOnly, NULL, NULL); 766 if (res_signed == errSecSuccess) { 767 // It is signed, now check if it's sandboxed 768 SecRequirementRef sandboxRequirementRef = NULL; 769 SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"), kSecCSDefaultFlags, &sandboxRequirementRef); 770 771 if (sandboxRequirementRef) { 772 OSStatus res_sandboxed = SecStaticCodeCheckValidityWithErrors(staticCodeRef, kSecCSBasicValidateOnly, sandboxRequirementRef, NULL); 773 if (res_sandboxed == errSecSuccess) { 774 // Yep, sandboxed 775 isSandboxed = 1; 776 } 777 778 CFRelease(sandboxRequirementRef); 779 } 780 } 781 782 CFRelease(staticCodeRef); 783 } 784 } 785 786 return isSandboxed == 1 ? YES : NO; 787 } 788 789 @end 790 791 #pragma mark --- JNI 792 793 /* 794 * Class: com_sun_glass_ui_mac_MacApplication 795 * Method: _initIDs 796 * Signature: ()V 797 */ 798 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1initIDs 799 (JNIEnv *env, jclass jClass, jboolean jDisableSyncRendering) 800 { 801 LOG("Java_com_sun_glass_ui_mac_MacApplication__1initIDs"); 802 803 disableSyncRendering = jDisableSyncRendering ? YES : NO; 804 805 jApplicationClass = (*env)->NewGlobalRef(env, jClass); 806 807 javaIDs.Application.createPixels = (*env)->GetStaticMethodID( 808 env, jClass, "createPixels", "(II[IFF)Lcom/sun/glass/ui/Pixels;"); 809 if ((*env)->ExceptionCheck(env)) return; 810 811 javaIDs.Application.getScaleFactor = (*env)->GetStaticMethodID( 812 env, jClass, "getScaleFactor", "(IIII)F"); 813 if ((*env)->ExceptionCheck(env)) return; 814 815 javaIDs.Application.reportException = (*env)->GetStaticMethodID( 816 env, jClass, "reportException", "(Ljava/lang/Throwable;)V"); 817 if ((*env)->ExceptionCheck(env)) return; 818 819 javaIDs.Application.enterNestedEventLoop = (*env)->GetStaticMethodID( 820 env, jClass, "enterNestedEventLoop", "()Ljava/lang/Object;"); 821 if ((*env)->ExceptionCheck(env)) return; 822 823 javaIDs.Application.leaveNestedEventLoop = (*env)->GetStaticMethodID( 824 env, jClass, "leaveNestedEventLoop", "(Ljava/lang/Object;)V"); 825 if ((*env)->ExceptionCheck(env)) return; 826 827 javaIDs.MacApplication.notifyApplicationDidTerminate = (*env)->GetMethodID( 828 env, jClass, "notifyApplicationDidTerminate", "()V"); 829 if ((*env)->ExceptionCheck(env)) return; 830 831 if (jRunnableRun == NULL) 832 { 833 jclass jcls = (*env)->FindClass(env, "java/lang/Runnable"); 834 if ((*env)->ExceptionCheck(env)) return; 835 jRunnableRun = (*env)->GetMethodID(env, jcls, "run", "()V"); 836 if ((*env)->ExceptionCheck(env)) return; 837 } 838 } 839 840 /* 841 * Class: com_sun_glass_ui_mac_MacApplication 842 * Method: _runLoop 843 * Signature: (Ljava/lang/ClassLoader;Ljava/lang/Runnable;Z)V 844 */ 845 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1runLoop 846 (JNIEnv *env, jobject japplication, jobject classLoader, 847 jobject jlaunchable, jboolean isTaskbarApplication) 848 { 849 LOG("Java_com_sun_glass_ui_mac_MacApplication__1runLoop"); 850 851 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 852 { 853 if ([NSThread isMainThread] == YES) 854 { 855 // 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"); 856 } 857 else 858 { 859 if ([[NSThread currentThread] name] == nil) 860 { 861 [[NSThread currentThread] setName:@"Main Java Thread"]; 862 } 863 } 864 865 GlassApplication *glass = [[GlassApplication alloc] initWithEnv:env application:japplication launchable:jlaunchable taskbarApplication:isTaskbarApplication classLoader:classLoader]; 866 if ([NSThread isMainThread] == YES) { 867 [glass runLoop: glass]; 868 } else { 869 [glass performSelectorOnMainThread:@selector(runLoop:) withObject:glass waitUntilDone:[NSThread isMainThread]]; 870 871 // wait for Cocoa to enter its UI runloop 872 while ([glass started] == NO) 873 { 874 LOG(" waiting for [glass started]"); 875 usleep(10000); 876 } 877 } 878 879 // at this point Java main thread is allowed to proceed, but Cocoa's UI thread entered its runloop, so the VM will not quit 880 } 881 [glasspool drain]; glasspool=nil; 882 GLASS_CHECK_EXCEPTION(env); 883 } 884 885 /* 886 * Class: com_sun_glass_ui_mac_MacApplication 887 * Method: _finishTerminating 888 * Signature: ()V 889 */ 890 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1finishTerminating 891 (JNIEnv *env, jobject japplication) 892 { 893 LOG("Java_com_sun_glass_ui_mac_MacApplication__1finishTerminating"); 894 895 if (isEmbedded) { 896 return; 897 } 898 899 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 900 { 901 [NSApp stop:nil]; 902 [NSApp hide:nil]; 903 904 // wake up the runloop one last time so that it can process the stop: 905 // request, even if the app is inactive currently 906 NSTimeInterval dummyEventTimestamp = [NSProcessInfo processInfo].systemUptime; 907 NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined 908 location: NSMakePoint(0,0) 909 modifierFlags: 0 910 timestamp: dummyEventTimestamp 911 windowNumber: 0 912 context: nil 913 subtype: 0 914 data1: 0 915 data2: 0]; 916 [NSApp postEvent: event atStart: NO]; 917 } 918 [glasspool drain]; glasspool=nil; 919 GLASS_CHECK_EXCEPTION(env); 920 } 921 922 /* 923 * Class: com_sun_glass_ui_mac_MacApplication 924 * Method: _enterNestedEventLoopImpl 925 * Signature: ()Ljava/lang/Object; 926 */ 927 JNIEXPORT jobject JNICALL Java_com_sun_glass_ui_mac_MacApplication__1enterNestedEventLoopImpl 928 (JNIEnv *env, jobject japplication) 929 { 930 LOG("Java_com_sun_glass_ui_mac_MacApplication__1enterNestedEventLoopImpl"); 931 932 jobject ret; 933 934 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 935 { 936 ret = [GlassApplication enterNestedEventLoopWithEnv:env]; 937 } 938 [glasspool drain]; glasspool=nil; 939 GLASS_CHECK_EXCEPTION(env); 940 941 return ret; 942 } 943 944 /* 945 * Class: com_sun_glass_ui_mac_MacApplication 946 * Method: _leaveNestedEventLoopImpl 947 * Signature: (Ljava/lang/Object;)V 948 */ 949 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1leaveNestedEventLoopImpl 950 (JNIEnv *env, jobject japplication, jobject retValue) 951 { 952 LOG("Java_com_sun_glass_ui_mac_MacApplication__1leaveNestedEventLoopImpl"); 953 954 NSAutoreleasePool *glasspool = [[NSAutoreleasePool alloc] init]; 955 { 956 [GlassApplication leaveNestedEventLoopWithEnv:env retValue:retValue]; 957 } 958 [glasspool drain]; glasspool=nil; 959 GLASS_CHECK_EXCEPTION(env); 960 } 961 962 /* 963 * Class: com_sun_glass_ui_Application 964 * Method: _submitForLaterInvocation 965 * Signature: (Ljava/lang/Runnable;)V 966 */ 967 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1submitForLaterInvocation 968 (JNIEnv *env, jobject japplication, jobject jRunnable) 969 { 970 //LOG("Java_com_sun_glass_ui_mac_MacApplication_submitForLaterInvocation"); 971 972 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 973 if (jEnv != NULL) 974 { 975 GlassRunnable *runnable = [[GlassRunnable alloc] initWithRunnable:(*env)->NewGlobalRef(env, jRunnable)]; 976 [runnable performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO]; 977 } 978 } 979 980 /* 981 * Class: com_sun_glass_ui_Application 982 * Method: _invokeAndWait 983 * Signature: (Ljava/lang/Runnable;)V 984 */ 985 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1invokeAndWait 986 (JNIEnv *env, jobject japplication, jobject jRunnable) 987 { 988 LOG("Java_com_sun_glass_ui_mac_MacApplication__1invokeAndWait"); 989 990 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 991 if (jEnv != NULL) 992 { 993 GlassRunnable *runnable = [[GlassRunnable alloc] initWithRunnable:(*env)->NewGlobalRef(env, jRunnable)]; 994 [runnable performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 995 } 996 } 997 998 /* 999 * Class: com_sun_glass_ui_mac_MacApplication 1000 * Method: _getRemoteLayerServerName 1001 * Signature: ()Ljava/lang/String; 1002 */ 1003 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_mac_MacApplication__1getRemoteLayerServerName 1004 (JNIEnv *env, jobject japplication) 1005 { 1006 LOG("Java_com_sun_glass_ui_mac_MacPasteboard__1getName"); 1007 1008 jstring name = NULL; 1009 1010 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 1011 GLASS_POOL_ENTER; 1012 { 1013 static mach_port_t remoteLayerServerPort = MACH_PORT_NULL; 1014 if (remoteLayerServerPort == MACH_PORT_NULL) 1015 { 1016 remoteLayerServerPort = RemoteLayerStartServer(); 1017 } 1018 NSString *remoteLayerServerName = RemoteLayerGetServerName(remoteLayerServerPort); 1019 name = (*env)->NewStringUTF(env, [remoteLayerServerName UTF8String]); 1020 } 1021 GLASS_POOL_EXIT; 1022 GLASS_CHECK_EXCEPTION(env); 1023 1024 return name; 1025 } 1026 1027 /* 1028 * Class: com_sun_glass_ui_mac_MacApplication 1029 * Method: staticScreen_getVideoRefreshPeriod 1030 * Signature: ()D 1031 */ 1032 JNIEXPORT jdouble JNICALL 1033 Java_com_sun_glass_ui_mac_MacApplication_staticScreen_1getVideoRefreshPeriod 1034 (JNIEnv *env, jobject jApplication) 1035 { 1036 LOG("Java_com_sun_glass_ui_mac_MacApplication__1getVideoRefreshPeriod"); 1037 1038 if (GlassDisplayLink != NULL) 1039 { 1040 double outRefresh = CVDisplayLinkGetActualOutputVideoRefreshPeriod(GlassDisplayLink); 1041 LOG("CVDisplayLinkGetActualOutputVideoRefreshPeriod: %f", outRefresh); 1042 return (outRefresh * 1000.0); // to millis 1043 } 1044 else 1045 { 1046 return 0.0; 1047 } 1048 } 1049 1050 /* 1051 * Class: com_sun_glass_ui_mac_MacApplication 1052 * Method: staticScreen_getScreens 1053 * Signature: ()[Lcom/sun/glass/ui/Screen; 1054 */ 1055 JNIEXPORT jobjectArray JNICALL Java_com_sun_glass_ui_mac_MacApplication_staticScreen_1getScreens 1056 (JNIEnv *env, jobject jApplication) 1057 { 1058 LOG("Java_com_sun_glass_ui_mac_MacApplication__1getScreens"); 1059 1060 jobjectArray screenArray = nil; 1061 1062 GLASS_POOL_ENTER; 1063 { 1064 screenArray = createJavaScreens(env); 1065 } 1066 GLASS_POOL_EXIT; 1067 GLASS_CHECK_EXCEPTION(env); 1068 1069 return screenArray; 1070 } 1071 1072 1073 /* 1074 * Class: com_sun_glass_ui_mac_MacApplication 1075 * Method: _supportsSystemMenu 1076 * Signature: ()Z; 1077 */ 1078 JNIEXPORT jboolean JNICALL Java_com_sun_glass_ui_mac_MacApplication__1supportsSystemMenu 1079 (JNIEnv *env, jobject japplication) 1080 { 1081 return !isEmbedded; 1082 } 1083 1084 /* 1085 * Class: com_sun_glass_ui_mac_MacApplication 1086 * Method: _hide 1087 * Signature: ()V; 1088 */ 1089 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1hide 1090 (JNIEnv *env, jobject japplication) 1091 { 1092 [NSApp hide:NSApp]; 1093 } 1094 1095 /* 1096 * Class: com_sun_glass_ui_mac_MacApplication 1097 * Method: _hideOtherApplications 1098 * Signature: ()V; 1099 */ 1100 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1hideOtherApplications 1101 (JNIEnv *env, jobject japplication) 1102 { 1103 [NSApp hideOtherApplications:NSApp]; 1104 } 1105 1106 /* 1107 * Class: com_sun_glass_ui_mac_MacApplication 1108 * Method: _unhideAllApplications 1109 * Signature: ()V; 1110 */ 1111 JNIEXPORT void JNICALL Java_com_sun_glass_ui_mac_MacApplication__1unhideAllApplications 1112 (JNIEnv *env, jobject japplication) 1113 { 1114 [NSApp unhideAllApplications:NSApp]; 1115 } 1116 1117 /* 1118 * Class: com_sun_glass_ui_mac_MacApplication 1119 * Method: _getDataDirectory 1120 * Signature: ()Ljava/lang/String; 1121 */ 1122 JNIEXPORT jstring JNICALL Java_com_sun_glass_ui_mac_MacApplication__1getDataDirectory 1123 (JNIEnv * env, jobject japplication) 1124 { 1125 jstring string = nil; 1126 1127 GLASS_ASSERT_MAIN_JAVA_THREAD(env); 1128 GLASS_POOL_ENTER; 1129 { 1130 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 1131 if (paths && [paths count] > 0) { 1132 string = (*env)->NewStringUTF(jEnv, [[paths lastObject] UTF8String]); 1133 } 1134 } 1135 GLASS_POOL_EXIT; 1136 GLASS_CHECK_EXCEPTION(env); 1137 1138 return string; 1139 } 1140 1141 /* 1142 * Class: com_sun_glass_ui_mac_MacApplication 1143 * Method: _getMacKey 1144 * Signature: (I)I 1145 */ 1146 JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacApplication__1getMacKey 1147 (JNIEnv *env, jclass jClass, jint code) 1148 { 1149 unsigned short macCode = 0; 1150 GetMacKey(code, &macCode); 1151 return (macCode & 0xFFFF); 1152 }