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