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