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