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