diff --git a/lib/mac/mac_util.h b/lib/mac/mac_util.h index 658fb67f4c..45406c4477 100644 --- a/lib/mac/mac_util.h +++ b/lib/mac/mac_util.h @@ -22,10 +22,34 @@ #include - double getTimeSinceBoot(void); - void getPathToThisApp(char* pathBuf, size_t bufSize); - void BringAppToFront(); - void BringAppWithPidToFront(pid_t pid); - pid_t getPidIfRunning(char * bundleID); + double getTimeSinceBoot(void); + void getPathToThisApp(char* pathBuf, size_t bufSize); + void BringAppToFront(); + void BringAppWithPidToFront(pid_t pid); + pid_t getPidIfRunning(char * bundleID); + + OSStatus GetPathToAppFromID(OSType creator, CFStringRef bundleID, char *path, size_t maxLen); + +// Macros to test OS version number on all versions of OS X without using deprecated Gestalt +// compareOSVersionTo(x, y) returns: +// -1 if the OS version we are running on is less than 10.x.y +// 0 if the OS version we are running on is equal to 10.x.y +// +1 if the OS version we are running on is lgreater than 10.x.y +// +#define MAKECFVERSIONNUMBER(x, y) floor(kCFCoreFoundationVersionNumber##x##_##y) +#define compareOSVersionTo(toMajor, toMinor) \ +(floor(kCFCoreFoundationVersionNumber) > MAKECFVERSIONNUMBER(toMajor, toMinor) ? 1 : \ +(floor(kCFCoreFoundationVersionNumber) < MAKECFVERSIONNUMBER(toMajor, toMinor) ? -1 : 0)) + +// Allow this to be built using Xcode 5.0.2 +#ifndef kCFCoreFoundationVersionNumber10_9 +#define kCFCoreFoundationVersionNumber10_9 855.11 +#endif +#ifndef kCFCoreFoundationVersionNumber10_10 +#define kCFCoreFoundationVersionNumber10_10 1151.16 +#endif +#ifndef kCFCoreFoundationVersionNumber10_11 +#define kCFCoreFoundationVersionNumber10_11 1253 +#endif #endif // _MAC_UTIL_H_ diff --git a/lib/mac/mac_util.mm b/lib/mac/mac_util.mm index 72da31a1a7..ba6f79b89a 100644 --- a/lib/mac/mac_util.mm +++ b/lib/mac/mac_util.mm @@ -19,8 +19,9 @@ #include "mac_util.h" #import - - +#define DLOPEN_NO_WARN +#include +#include // Returns time in seconds since system was booted @@ -56,3 +57,64 @@ pid_t getPidIfRunning(char * bundleID) { } return 0; } + + +// Find the path to the app with the bundle identifier and (optionally) creator code. +// The creator code can be NULL. +OSStatus GetPathToAppFromID(OSType creator, CFStringRef bundleID, char *path, size_t maxLen) { + CFURLRef appURL = NULL; + OSErr err; + + // We must launch the System Events application for the target user + err = noErr; + *path = '\0'; + + // LSCopyApplicationURLsForBundleIdentifier is not available before OS 10.10 + CFArrayRef (*LSCopyAppURLsForBundleID)(CFStringRef, CFErrorRef) = NULL; + void *LSlib = dlopen("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/LaunchServices", RTLD_NOW); + if (LSlib) { + LSCopyAppURLsForBundleID = (CFArrayRef(*)(CFStringRef, CFErrorRef)) dlsym(LSlib, "LSCopyApplicationURLsForBundleIdentifier"); + } + if (LSCopyAppURLsForBundleID == NULL) { + err = fnfErr; + } + +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + if (err != noErr) { // LSCopyAppURLsForBundleID == NULL +//LSFindApplicationForInfo is deprecated in OS 10.10, so may not be available in the future + OSStatus (*LSFindAppForInfo)(OSType, CFStringRef, CFStringRef, FSRef*, CFURLRef*) = NULL; + if (LSlib) { + LSFindAppForInfo = (OSStatus(*)(OSType, CFStringRef, CFStringRef, FSRef*, CFURLRef*)) + dlsym(LSlib, "LSFindApplicationForInfo"); + } + if (LSFindAppForInfo == NULL) { + return fnfErr; + } + err = (*LSFindAppForInfo)(creator, bundleID, NULL, NULL, &appURL); + } else // if (LSCopyApplicationURLsForBundleIdentifier != NULL) +#endif + { + if (err == noErr) { + CFArrayRef appRefs = (*LSCopyAppURLsForBundleID)(bundleID, NULL); + if (appRefs == NULL) { + err = fnfErr; + } else { + appURL = (CFURLRef)CFArrayGetValueAtIndex(appRefs, 0); + CFRelease(appRefs); + } + } + if (err != noErr) { + return err; + } + } // end if (LSCopyApplicationURLsForBundleIdentifier != NULL) + + if (err == noErr) { + CFStringRef CFPath = CFURLCopyFileSystemPath(appURL, kCFURLPOSIXPathStyle); + CFStringGetCString(CFPath, path, maxLen, kCFStringEncodingUTF8); + CFRelease(CFPath); + } + if (appURL) { + CFRelease(appURL); + } + return err; +}