diff options
Diffstat (limited to 'tk8.6/macosx/tkMacOSXInit.c')
-rw-r--r-- | tk8.6/macosx/tkMacOSXInit.c | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/tk8.6/macosx/tkMacOSXInit.c b/tk8.6/macosx/tkMacOSXInit.c new file mode 100644 index 0000000..33a60f2 --- /dev/null +++ b/tk8.6/macosx/tkMacOSXInit.c @@ -0,0 +1,608 @@ +/* + * tkMacOSXInit.c -- + * + * This file contains Mac OS X -specific interpreter initialization + * functions. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkMacOSXPrivate.h" + +#include <sys/stat.h> +#include <sys/utsname.h> +#include <dlfcn.h> +#include <objc/objc-auto.h> + +static char tkLibPath[PATH_MAX + 1] = ""; + +/* + * If the App is in an App package, then we want to add the Scripts directory + * to the auto_path. + */ + +static char scriptPath[PATH_MAX + 1] = ""; + +long tkMacOSXMacOSXVersion = 0; + +#pragma mark TKApplication(TKInit) + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#define NSTextInputContextKeyboardSelectionDidChangeNotification @"NSTextInputContextKeyboardSelectionDidChangeNotification" +static void keyboardChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { + [[NSNotificationCenter defaultCenter] postNotificationName:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil userInfo:nil]; +} +#endif + +@interface TKApplication(TKKeyboard) +- (void) keyboardChanged: (NSNotification *) notification; +@end + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 +#define TKApplication_NSApplicationDelegate <NSApplicationDelegate> +#else +#define TKApplication_NSApplicationDelegate +#endif +@interface TKApplication(TKWindowEvent) TKApplication_NSApplicationDelegate +- (void) _setupWindowNotifications; +@end + +@interface TKApplication(TKMenus) +- (void) _setupMenus; +@end + +@implementation TKApplication +@synthesize poolProtected = _poolProtected; +@end + +@implementation TKApplication(TKInit) +- (void) _resetAutoreleasePool +{ + if(![self poolProtected]) { + [_mainPool drain]; + _mainPool = [NSAutoreleasePool new]; + } +} +#ifdef TK_MAC_DEBUG_NOTIFICATIONS +- (void) _postedNotification: (NSNotification *) notification +{ + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +} +#endif + +- (void) _setupApplicationNotifications +{ + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; +#define observe(n, s) \ + [nc addObserver:self selector:@selector(s) name:(n) object:nil] + observe(NSApplicationDidBecomeActiveNotification, applicationActivate:); + observe(NSApplicationDidResignActiveNotification, applicationDeactivate:); + observe(NSApplicationDidUnhideNotification, applicationShowHide:); + observe(NSApplicationDidHideNotification, applicationShowHide:); + observe(NSApplicationDidChangeScreenParametersNotification, displayChanged:); + observe(NSTextInputContextKeyboardSelectionDidChangeNotification, keyboardChanged:); +#undef observe +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), NULL, &keyboardChanged, kTISNotifySelectedKeyboardInputSourceChanged, NULL, CFNotificationSuspensionBehaviorCoalesce); +#endif +} + +- (void) _setupEventLoop +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + [self finishLaunching]; + [self setWindowsNeedUpdate:YES]; + [pool drain]; +} + +- (void) _setup: (Tcl_Interp *) interp +{ + _eventInterp = interp; + _mainPool = [NSAutoreleasePool new]; + [NSApp setPoolProtected:NO]; + _defaultMainMenu = nil; + [self _setupMenus]; + [self setDelegate:self]; +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_postedNotification:) name:nil object:nil]; +#endif + [self _setupWindowNotifications]; + [self _setupApplicationNotifications]; +} + +- (NSString *) tkFrameworkImagePath: (NSString *) image +{ + NSString *path = nil; + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + if (tkLibPath[0] != '\0') { + path = [[NSBundle bundleWithPath:[[NSString stringWithUTF8String: + tkLibPath] stringByAppendingString:@"/../.."]] + pathForImageResource:image]; + } + if (!path) { + const char *tk_library = Tcl_GetVar2(_eventInterp, "tk_library", NULL, + TCL_GLOBAL_ONLY); + + if (tk_library) { + NSFileManager *fm = [NSFileManager defaultManager]; + + path = [[NSString stringWithUTF8String:tk_library] + stringByAppendingFormat:@"/%@", image]; + if (![fm isReadableFileAtPath:path]) { + path = [[NSString stringWithUTF8String:tk_library] + stringByAppendingFormat:@"/../macosx/%@", image]; + if (![fm isReadableFileAtPath:path]) { + path = nil; + } + } + } + } +#ifdef TK_MAC_DEBUG + if (!path && getenv("TK_SRCROOT")) { + path = [[NSString stringWithUTF8String:getenv("TK_SRCROOT")] + stringByAppendingFormat:@"/macosx/%@", image]; + if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) { + path = nil; + } + } +#endif + [path retain]; + [pool drain]; + return path; +} +@end + +#pragma mark - + +/* + *---------------------------------------------------------------------- + * + * DoWindowActivate -- + * + * Idle handler that sets the application icon to the generic Tk icon. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +SetApplicationIcon( + ClientData clientData) +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + NSString *path = [NSApp tkFrameworkImagePath:@"Tk.icns"]; + if (path) { + NSImage *image = [[NSImage alloc] initWithContentsOfFile:path]; + if (image) { + [NSApp setApplicationIconImage:image]; + [image release]; + } + } + [pool drain]; +} + +/* + *---------------------------------------------------------------------- + * + * TkpInit -- + * + * Performs Mac-specific interpreter initialization related to the + * tk_library variable. + * + * Results: + * Returns a standard Tcl result. Leaves an error message or result in + * the interp's result. + * + * Side effects: + * Sets "tk_library" Tcl variable, runs "tk.tcl" script. + * + *---------------------------------------------------------------------- + */ + +int +TkpInit( + Tcl_Interp *interp) +{ + static int initialized = 0; + + /* + * Since it is possible for TkInit to be called multiple times and we + * don't want to do the following initialization multiple times we protect + * against doing it more than once. + */ + + if (!initialized) { + int bundledExecutable = 0; + CFBundleRef bundleRef; + CFURLRef bundleUrl = NULL; + struct utsname name; + struct stat st; + + initialized = 1; + + /* + * Initialize/check OS version variable for runtime checks. + */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +# error Mac OS X 10.5 required +#endif + + if (!uname(&name)) { + tkMacOSXMacOSXVersion = (strtod(name.release, NULL) + 96) * 10; + } + /*Check for new versioning scheme on Yosemite (10.10) and later.*/ + if (MAC_OS_X_VERSION_MIN_REQUIRED > 100000) { + tkMacOSXMacOSXVersion = MAC_OS_X_VERSION_MIN_REQUIRED/100; + } + if (tkMacOSXMacOSXVersion && MAC_OS_X_VERSION_MIN_REQUIRED < 100000 && + tkMacOSXMacOSXVersion/10 < MAC_OS_X_VERSION_MIN_REQUIRED/10) { + Tcl_Panic("Mac OS X 10.%d or later required !", + (MAC_OS_X_VERSION_MIN_REQUIRED/10)-100); + } + + +#ifdef TK_FRAMEWORK + /* + * When Tk is in a framework, force tcl_findLibrary to look in the + * framework scripts directory. + * FIXME: Should we come up with a more generic way of doing this? + */ + + if (Tcl_MacOSXOpenVersionedBundleResources(interp, + "com.tcltk.tklibrary", TK_FRAMEWORK_VERSION, 0, PATH_MAX, + tkLibPath) != TCL_OK) { + TkMacOSXDbgMsg("Tcl_MacOSXOpenVersionedBundleResources failed"); + } +#endif + + { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + [[NSUserDefaults standardUserDefaults] registerDefaults: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], + @"_NSCanWrapButtonTitles", + [NSNumber numberWithInt:-1], + @"NSStringDrawingTypesetterBehavior", + nil]]; + [TKApplication sharedApplication]; + [pool drain]; + [NSApp _setup:interp]; + } + + /* Check whether we are a bundled executable: */ + bundleRef = CFBundleGetMainBundle(); + if (bundleRef) { + bundleUrl = CFBundleCopyBundleURL(bundleRef); + } + if (bundleUrl) { + /* + * A bundled executable is two levels down from its main bundle + * directory (e.g. Wish.app/Contents/MacOS/Wish), whereas an + * unbundled executable's main bundle directory is just the + * directory containing the executable. So to check whether we are + * bundled, we delete the last three path components of the + * executable's url and compare the resulting url with the main + * bundle url. + */ + + int j = 3; + CFURLRef url = CFBundleCopyExecutableURL(bundleRef); + + while (url && j--) { + CFURLRef parent = + CFURLCreateCopyDeletingLastPathComponent(NULL, url); + + CFRelease(url); + url = parent; + } + if (url) { + bundledExecutable = CFEqual(bundleUrl, url); + CFRelease(url); + } + CFRelease(bundleUrl); + } + + if (!bundledExecutable) { + /* + * If we are loaded into an executable that is not a bundled + * application, the window server does not let us come to the + * foreground. For such an executable, notify the window server + * that we are now a full GUI application. + */ + + OSStatus err = procNotFound; + ProcessSerialNumber psn = { 0, kCurrentProcess }; + + err = ChkErr(TransformProcessType, &psn, + kProcessTransformToForegroundApplication); + + /* + * Set application icon to generic Tk icon, do it at idle time + * instead of now to ensure tk_library is setup. + */ + + Tcl_DoWhenIdle(SetApplicationIcon, NULL); + } + + { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + [NSApp _setupEventLoop]; + TkMacOSXInitAppleEvents(interp); + TkMacOSXUseAntialiasedText(interp, -1); + TkMacOSXInitCGDrawing(interp, TRUE, 0); + [pool drain]; + } + + /* + * FIXME: Close stdin & stdout for remote debugging otherwise we will + * fight with gdb for stdin & stdout + */ + + if (getenv("XCNOSTDIN") != NULL) { + close(0); + close(1); + } + + /* + * If we don't have a TTY and stdin is a special character file of + * length 0, (e.g. /dev/null, which is what Finder sets when double + * clicking Wish) then use the Tk based console interpreter. + */ + + if (getenv("TK_CONSOLE") || + (!isatty(0) && (fstat(0, &st) || + (S_ISCHR(st.st_mode) && st.st_blocks == 0)))) { + Tk_InitConsoleChannels(interp); + Tcl_RegisterChannel(interp, Tcl_GetStdChannel(TCL_STDIN)); + Tcl_RegisterChannel(interp, Tcl_GetStdChannel(TCL_STDOUT)); + Tcl_RegisterChannel(interp, Tcl_GetStdChannel(TCL_STDERR)); + + /* + * Only show the console if we don't have a startup script + * and tcl_interactive hasn't been set already. + */ + + if (Tcl_GetStartupScript(NULL) == NULL) { + const char *intvar = Tcl_GetVar2(interp, + "tcl_interactive", NULL, TCL_GLOBAL_ONLY); + + if (intvar == NULL) { + Tcl_SetVar2(interp, "tcl_interactive", NULL, "1", + TCL_GLOBAL_ONLY); + } + } + if (Tk_CreateConsoleWindow(interp) == TCL_ERROR) { + return TCL_ERROR; + } + } + } + + Tk_MacOSXSetupTkNotifier(); + + if (tkLibPath[0] != '\0') { + Tcl_SetVar2(interp, "tk_library", NULL, tkLibPath, TCL_GLOBAL_ONLY); + } + + if (scriptPath[0] != '\0') { + Tcl_SetVar2(interp, "auto_path", NULL, scriptPath, + TCL_GLOBAL_ONLY|TCL_LIST_ELEMENT|TCL_APPEND_VALUE); + } + + Tcl_CreateObjCommand(interp, "::tk::mac::standardAboutPanel", + TkMacOSXStandardAboutPanelObjCmd, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tk::mac::iconBitmap", + TkMacOSXIconBitmapObjCmd, NULL, NULL); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetAppName -- + * + * Retrieves the name of the current application from a platform specific + * location. For Unix, the application name is the tail of the path + * contained in the tcl variable argv0. + * + * Results: + * Returns the application name in the given Tcl_DString. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpGetAppName( + Tcl_Interp *interp, + Tcl_DString *namePtr) /* A previously initialized Tcl_DString. */ +{ + const char *p, *name; + + name = Tcl_GetVar2(interp, "argv0", NULL, TCL_GLOBAL_ONLY); + if ((name == NULL) || (*name == 0)) { + name = "tk"; + } else { + p = strrchr(name, '/'); + if (p != NULL) { + name = p+1; + } + } + Tcl_DStringAppend(namePtr, name, -1); +} + +/* + *---------------------------------------------------------------------- + * + * TkpDisplayWarning -- + * + * This routines is called from Tk_Main to display warning messages that + * occur during startup. + * + * Results: + * None. + * + * Side effects: + * Generates messages on stdout. + * + *---------------------------------------------------------------------- + */ + +void +TkpDisplayWarning( + const char *msg, /* Message to be displayed. */ + const char *title) /* Title of warning. */ +{ + Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR); + + if (errChannel) { + Tcl_WriteChars(errChannel, title, -1); + Tcl_WriteChars(errChannel, ": ", 2); + Tcl_WriteChars(errChannel, msg, -1); + Tcl_WriteChars(errChannel, "\n", 1); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXDefaultStartupScript -- + * + * On MacOS X, we look for a file in the Resources/Scripts directory + * called AppMain.tcl and if found, we set argv[1] to that, so that the + * rest of the code will find it, and add the Scripts folder to the + * auto_path. If we don't find the startup script, we just bag it, + * assuming the user is starting up some other way. + * + * Results: + * None. + * + * Side effects: + * Tcl_SetStartupScript() called when AppMain.tcl found. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE void +TkMacOSXDefaultStartupScript(void) +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + CFBundleRef bundleRef = CFBundleGetMainBundle(); + + if (bundleRef != NULL) { + CFURLRef appMainURL = CFBundleCopyResourceURL(bundleRef, + CFSTR("AppMain"), CFSTR("tcl"), CFSTR("Scripts")); + + if (appMainURL != NULL) { + CFURLRef scriptFldrURL; + char startupScript[PATH_MAX + 1]; + + if (CFURLGetFileSystemRepresentation (appMainURL, true, + (unsigned char *) startupScript, PATH_MAX)) { + Tcl_SetStartupScript(Tcl_NewStringObj(startupScript,-1), NULL); + scriptFldrURL = CFURLCreateCopyDeletingLastPathComponent(NULL, + appMainURL); + if (scriptFldrURL != NULL) { + CFURLGetFileSystemRepresentation(scriptFldrURL, true, + (unsigned char *) scriptPath, PATH_MAX); + CFRelease(scriptFldrURL); + } + } + CFRelease(appMainURL); + } + } + [pool drain]; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetNamedSymbol -- + * + * Dynamically acquire address of a named symbol from a loaded dynamic + * library, so that we can use API that may not be available on all OS + * versions. + * + * Results: + * Address of given symbol or NULL if unavailable. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE void* +TkMacOSXGetNamedSymbol( + const char* module, + const char* symbol) +{ + void *addr = dlsym(RTLD_NEXT, symbol); + if (!addr) { + (void) dlerror(); /* Clear dlfcn error state */ + } + return addr; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetStringObjFromCFString -- + * + * Get a string object from a CFString as efficiently as possible. + * + * Results: + * New string object or NULL if conversion failed. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE Tcl_Obj* +TkMacOSXGetStringObjFromCFString( + CFStringRef str) +{ + Tcl_Obj *obj = NULL; + const char *c = CFStringGetCStringPtr(str, kCFStringEncodingUTF8); + + if (c) { + obj = Tcl_NewStringObj(c, -1); + } else { + CFRange all = CFRangeMake(0, CFStringGetLength(str)); + CFIndex len; + + if (CFStringGetBytes(str, all, kCFStringEncodingUTF8, 0, false, NULL, + 0, &len) > 0 && len < INT_MAX) { + obj = Tcl_NewObj(); + Tcl_SetObjLength(obj, len); + CFStringGetBytes(str, all, kCFStringEncodingUTF8, 0, false, + (UInt8*) obj->bytes, len, NULL); + } + } + return obj; +} + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |