Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/sun/osxapp/NSApplicationAWT.m
38829 views
/*1* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import "NSApplicationAWT.h"2627#import <objc/runtime.h>28#import <JavaRuntimeSupport/JavaRuntimeSupport.h>2930#import "PropertiesUtilities.h"31#import "ThreadUtilities.h"32#import "QueuingApplicationDelegate.h"33#import "AWTIconData.h"343536static BOOL sUsingDefaultNIB = YES;37static NSString *SHARED_FRAMEWORK_BUNDLE = @"/System/Library/Frameworks/JavaVM.framework";38static id <NSApplicationDelegate> applicationDelegate = nil;39static QueuingApplicationDelegate * qad = nil;4041// Flag used to indicate to the Plugin2 event synthesis code to do a postEvent instead of sendEvent42BOOL postEventDuringEventSynthesis = NO;4344@implementation NSApplicationAWT4546- (id) init47{48// Headless: NO49// Embedded: NO50// Multiple Calls: NO51// Caller: +[NSApplication sharedApplication]5253AWT_ASSERT_APPKIT_THREAD;54fApplicationName = nil;55dummyEventTimestamp = 0.0;56seenDummyEventLock = nil;575859// NSApplication will call _RegisterApplication with the application's bundle, but there may not be one.60// So, we need to call it ourselves to ensure the app is set up properly.61[self registerWithProcessManager];6263return [super init];64}6566- (void)dealloc67{68[fApplicationName release];69fApplicationName = nil;7071[super dealloc];72}7374- (void)finishLaunching75{76AWT_ASSERT_APPKIT_THREAD;7778JNIEnv *env = [ThreadUtilities getJNIEnv];7980// Get default nib file location81// NOTE: This should learn about the current java.version. Probably best thru82// the Makefile system's -DFRAMEWORK_VERSION define. Need to be able to pass this83// thru to PB from the Makefile system and for local builds.84NSString *defaultNibFile = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.application.nib" withEnv:env];85if (!defaultNibFile) {86NSBundle *javaBundle = [NSBundle bundleWithPath:SHARED_FRAMEWORK_BUNDLE];87defaultNibFile = [javaBundle pathForResource:@"DefaultApp" ofType:@"nib"];88} else {89sUsingDefaultNIB = NO;90}9192[NSBundle loadNibFile:defaultNibFile externalNameTable: [NSDictionary dictionaryWithObject:self forKey:@"NSOwner"] withZone:nil];9394// Set user defaults to not try to parse application arguments.95NSUserDefaults * defs = [NSUserDefaults standardUserDefaults];96NSDictionary * noOpenDict = [NSDictionary dictionaryWithObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];97[defs registerDefaults:noOpenDict];9899// Fix up the dock icon now that we are registered with CAS and the Dock.100[self setDockIconWithEnv:env];101102// If we are using our nib (the default application NIB) we need to put the app name into103// the application menu, which has placeholders for the name.104if (sUsingDefaultNIB) {105NSUInteger i, itemCount;106NSMenu *theMainMenu = [NSApp mainMenu];107108// First submenu off the main menu is the application menu.109NSMenuItem *appMenuItem = [theMainMenu itemAtIndex:0];110NSMenu *appMenu = [appMenuItem submenu];111itemCount = [appMenu numberOfItems];112113for (i = 0; i < itemCount; i++) {114NSMenuItem *anItem = [appMenu itemAtIndex:i];115NSString *oldTitle = [anItem title];116[anItem setTitle:[NSString stringWithFormat:oldTitle, fApplicationName]];117}118}119120if (applicationDelegate) {121[self setDelegate:applicationDelegate];122} else {123qad = [QueuingApplicationDelegate sharedDelegate];124[self setDelegate:qad];125}126127[super finishLaunching];128129// inform any interested parties that the AWT has arrived and is pumping130[[NSNotificationCenter defaultCenter] postNotificationName:JNFRunLoopDidStartNotification object:self];131}132133- (void) registerWithProcessManager134{135// Headless: NO136// Embedded: NO137// Multiple Calls: NO138// Caller: -[NSApplicationAWT init]139140AWT_ASSERT_APPKIT_THREAD;141JNIEnv *env = [ThreadUtilities getJNIEnv];142143char envVar[80];144145// The following environment variable is set from the -Xdock:name param. It should be UTF8.146snprintf(envVar, sizeof(envVar), "APP_NAME_%d", getpid());147char *appName = getenv(envVar);148if (appName != NULL) {149fApplicationName = [NSString stringWithUTF8String:appName];150unsetenv(envVar);151}152153// If it wasn't specified as an argument, see if it was specified as a system property.154if (fApplicationName == nil) {155fApplicationName = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.application.name" withEnv:env];156}157158// If we STILL don't have it, the app name is retrieved from an environment variable (set in java.c) It should be UTF8.159if (fApplicationName == nil) {160char mainClassEnvVar[80];161snprintf(mainClassEnvVar, sizeof(mainClassEnvVar), "JAVA_MAIN_CLASS_%d", getpid());162char *mainClass = getenv(mainClassEnvVar);163if (mainClass != NULL) {164fApplicationName = [NSString stringWithUTF8String:mainClass];165unsetenv(mainClassEnvVar);166167NSRange lastPeriod = [fApplicationName rangeOfString:@"." options:NSBackwardsSearch];168if (lastPeriod.location != NSNotFound) {169fApplicationName = [fApplicationName substringFromIndex:lastPeriod.location + 1];170}171}172}173174// The dock name is nil for double-clickable Java apps (bundled and Web Start apps)175// When that happens get the display name, and if that's not available fall back to176// CFBundleName.177NSBundle *mainBundle = [NSBundle mainBundle];178if (fApplicationName == nil) {179fApplicationName = (NSString *)[mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];180181if (fApplicationName == nil) {182fApplicationName = (NSString *)[mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey];183184if (fApplicationName == nil) {185fApplicationName = (NSString *)[mainBundle objectForInfoDictionaryKey: (NSString *)kCFBundleExecutableKey];186187if (fApplicationName == nil) {188// Name of last resort is the last part of the applicatoin name without the .app (consistent with CopyProcessName)189fApplicationName = [[mainBundle bundlePath] lastPathComponent];190191if ([fApplicationName hasSuffix:@".app"]) {192fApplicationName = [fApplicationName stringByDeletingPathExtension];193}194}195}196}197}198199// We're all done trying to determine the app name. Hold on to it.200[fApplicationName retain];201202NSDictionary *registrationOptions = [NSMutableDictionary dictionaryWithObject:fApplicationName forKey:@"JRSAppNameKey"];203204NSString *launcherType = [PropertiesUtilities javaSystemPropertyForKey:@"sun.java.launcher" withEnv:env];205if ([@"SUN_STANDARD" isEqualToString:launcherType]) {206[registrationOptions setValue:[NSNumber numberWithBool:YES] forKey:@"JRSAppIsCommandLineKey"];207}208209NSString *uiElementProp = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.UIElement" withEnv:env];210if ([@"true" isCaseInsensitiveLike:uiElementProp]) {211[registrationOptions setValue:[NSNumber numberWithBool:YES] forKey:@"JRSAppIsUIElementKey"];212}213214NSString *backgroundOnlyProp = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.BackgroundOnly" withEnv:env];215if ([@"true" isCaseInsensitiveLike:backgroundOnlyProp]) {216[registrationOptions setValue:[NSNumber numberWithBool:YES] forKey:@"JRSAppIsBackgroundOnlyKey"];217}218219// TODO replace with direct call220// [JRSAppKitAWT registerAWTAppWithOptions:registrationOptions];221// and remove below transform/activate/run hack222223id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");224SEL registerSel = @selector(registerAWTAppWithOptions:);225if ([jrsAppKitAWTClass respondsToSelector:registerSel]) {226[jrsAppKitAWTClass performSelector:registerSel withObject:registrationOptions];227return;228}229230// HACK BEGIN231// The following is necessary to make the java process behave like a232// proper foreground application...233[JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){234ProcessSerialNumber psn;235GetCurrentProcess(&psn);236TransformProcessType(&psn, kProcessTransformToForegroundApplication);237238[NSApp activateIgnoringOtherApps:YES];239[NSApp run];240}];241// HACK END242}243244- (void) setDockIconWithEnv:(JNIEnv *)env {245NSString *theIconPath = nil;246247// The following environment variable is set in java.c. It is probably UTF8.248char envVar[80];249snprintf(envVar, sizeof(envVar), "APP_ICON_%d", getpid());250char *appIcon = getenv(envVar);251if (appIcon != NULL) {252theIconPath = [NSString stringWithUTF8String:appIcon];253unsetenv(envVar);254}255256if (theIconPath == nil) {257theIconPath = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.application.icon" withEnv:env];258}259260// Use the path specified to get the icon image261NSImage* iconImage = nil;262if (theIconPath != nil) {263iconImage = [[NSImage alloc] initWithContentsOfFile:theIconPath];264}265266// If no icon file was specified or we failed to get the icon image267// and there is no bundle's icon, then use the default icon268if (iconImage == nil) {269NSString* bundleIcon = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFile"];270if (bundleIcon == nil) {271NSData* iconData;272iconData = [[NSData alloc] initWithBytesNoCopy: sAWTIconData length: sizeof(sAWTIconData) freeWhenDone: NO];273iconImage = [[NSImage alloc] initWithData: iconData];274[iconData release];275}276}277278// Set up the dock icon if we have an icon image.279if (iconImage != nil) {280[NSApp setApplicationIconImage:iconImage];281[iconImage release];282}283}284285+ (void) runAWTLoopWithApp:(NSApplication*)app {286NSAutoreleasePool *pool = [NSAutoreleasePool new];287288// Make sure that when we run in AWTRunLoopMode we don't exit randomly289[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:[JNFRunLoop javaRunLoopMode]];290291do {292@try {293[app run];294} @catch (NSException* e) {295NSLog(@"Apple AWT Startup Exception: %@", [e description]);296NSLog(@"Apple AWT Restarting Native Event Thread");297298[app stop:app];299}300} while (YES);301302[pool drain];303}304305- (BOOL)usingDefaultNib {306return sUsingDefaultNIB;307}308309- (void)orderFrontStandardAboutPanelWithOptions:(NSDictionary *)optionsDictionary {310if (!optionsDictionary) {311optionsDictionary = [NSMutableDictionary dictionaryWithCapacity:2];312[optionsDictionary setValue:[[[[[NSApp mainMenu] itemAtIndex:0] submenu] itemAtIndex:0] title] forKey:@"ApplicationName"];313if (![NSImage imageNamed:@"NSApplicationIcon"]) {314[optionsDictionary setValue:[NSApp applicationIconImage] forKey:@"ApplicationIcon"];315}316}317318[super orderFrontStandardAboutPanelWithOptions:optionsDictionary];319}320321#define DRAGMASK (NSMouseMovedMask | NSLeftMouseDraggedMask | NSRightMouseDownMask | NSRightMouseDraggedMask | NSLeftMouseUpMask | NSRightMouseUpMask | NSFlagsChangedMask | NSKeyDownMask)322323- (NSEvent *)nextEventMatchingMask:(NSUInteger)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)deqFlag {324if (mask == DRAGMASK && [((NSString *)kCFRunLoopDefaultMode) isEqual:mode]) {325postEventDuringEventSynthesis = YES;326}327328NSEvent *event = [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue: deqFlag];329postEventDuringEventSynthesis = NO;330331return event;332}333334// NSTimeInterval has microseconds precision335#define TS_EQUAL(ts1, ts2) (fabs((ts1) - (ts2)) < 1e-6)336337- (void)sendEvent:(NSEvent *)event338{339if ([event type] == NSApplicationDefined && TS_EQUAL([event timestamp], dummyEventTimestamp) && [event subtype] == 0) {340[seenDummyEventLock lockWhenCondition:NO];341[seenDummyEventLock unlockWithCondition:YES];342} else if ([event type] == NSApplicationDefined && [event subtype] == 777) {343void (^block)() = (void (^)()) [event data1];344block();345[block release];346} else if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) {347// Cocoa won't send us key up event when releasing a key while Cmd is down,348// so we have to do it ourselves.349[[self keyWindow] sendEvent:event];350} else {351[super sendEvent:event];352}353}354355/*356* Posts the block to the AppKit event queue which will be executed357* on the main AppKit loop.358* While running nested loops this event will be ignored.359*/360- (void)postRunnableEvent:(void (^)())block361{362void (^copy)() = [block copy];363NSInteger encode = (NSInteger) copy;364NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];365NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined366location: NSMakePoint(0,0)367modifierFlags: 0368timestamp: 0369windowNumber: 0370context: nil371subtype: 777372data1: encode373data2: 0];374375[NSApp postEvent: event atStart: NO];376[pool drain];377}378379- (void)postDummyEvent:(bool)useCocoa {380seenDummyEventLock = [[NSConditionLock alloc] initWithCondition:NO];381dummyEventTimestamp = [NSProcessInfo processInfo].systemUptime;382383NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];384NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined385location: NSMakePoint(0,0)386modifierFlags: 0387timestamp: dummyEventTimestamp388windowNumber: 0389context: nil390subtype: 0391data1: 0392data2: 0];393if (useCocoa) {394[NSApp postEvent:event atStart:NO];395} else {396ProcessSerialNumber psn;397GetCurrentProcess(&psn);398CGEventPostToPSN(&psn, [event CGEvent]);399}400[pool drain];401}402403- (void)waitForDummyEvent:(double)timeout {404bool unlock = true;405if (timeout >= 0) {406double sec = timeout / 1000;407unlock = [seenDummyEventLock lockWhenCondition:YES408beforeDate:[NSDate dateWithTimeIntervalSinceNow:sec]];409} else {410[seenDummyEventLock lockWhenCondition:YES];411}412if (unlock) {413[seenDummyEventLock unlock];414}415[seenDummyEventLock release];416417seenDummyEventLock = nil;418}419420@end421422423void OSXAPP_SetApplicationDelegate(id <NSApplicationDelegate> delegate)424{425AWT_ASSERT_APPKIT_THREAD;426applicationDelegate = delegate;427428if (NSApp != nil) {429[NSApp setDelegate: applicationDelegate];430431if (applicationDelegate && qad) {432[qad processQueuedEventsWithTargetDelegate: applicationDelegate];433qad = nil;434}435}436}437438439440