1// This file is part of BOINC.
2// http://boinc.berkeley.edu
3// Copyright (C) 2017 University of California
4//
5// BOINC is free software; you can redistribute it and/or modify it
6// under the terms of the GNU Lesser General Public License
7// as published by the Free Software Foundation,
8// either version 3 of the License, or (at your option) any later version.
9//
10// BOINC is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17
18//  mac_util.mm
19
20#include "mac_util.h"
21#import <Cocoa/Cocoa.h>
22#define DLOPEN_NO_WARN
23#include <mach-o/dyld.h>
24#include <dlfcn.h>
25
26
27// Returns time in seconds since system was booted
28//
29double getTimeSinceBoot() {
30    return [[NSProcessInfo processInfo] systemUptime];
31}
32
33
34void getPathToThisApp(char* pathBuf, size_t bufSize) {
35    // Get the app's main bundle
36    NSBundle *main = [NSBundle mainBundle];
37    NSString *thePath = [main bundlePath];
38    strlcpy(pathBuf, [thePath UTF8String], bufSize);
39}
40
41
42void BringAppToFront() {
43    [ [NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps | NSApplicationActivateAllWindows ];
44}
45
46
47void BringAppWithPidToFront(pid_t pid) {
48    NSRunningApplication * theRunningApp = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
49    if (theRunningApp) {
50        [ theRunningApp activateWithOptions:NSApplicationActivateIgnoringOtherApps | NSApplicationActivateAllWindows ];
51    }
52}
53
54
55pid_t getActiveAppPid() {
56    NSArray * runningApps = [[NSWorkspace sharedWorkspace] runningApplications];
57    unsigned int i;
58    unsigned int n = [ runningApps count ];
59    for (i=0; i<n; i++) {
60        NSRunningApplication * theApp = (NSRunningApplication *)[ runningApps objectAtIndex:i ];
61        if ([ theApp isActive ]) {
62            return [theApp processIdentifier];
63        }
64    }
65    return 0;
66}
67
68
69pid_t getPidIfRunning(char * bundleID) {
70    NSString *NSBundleID = [[NSString alloc] initWithUTF8String:bundleID];
71    NSArray * runningApps = [NSRunningApplication runningApplicationsWithBundleIdentifier:NSBundleID];
72    if (runningApps) {
73        if ([runningApps count] > 0) {
74            return [((NSRunningApplication *)[runningApps firstObject]) processIdentifier];
75        }
76    }
77    return 0;
78}
79
80
81// Find the path to the app with the bundle identifier and (optionally) creator code.
82// The creator code can be NULL.
83OSStatus GetPathToAppFromID(OSType creator, CFStringRef bundleID, char *path, size_t maxLen) {
84    CFURLRef                appURL = NULL;
85    OSErr                   err;
86
87    // We must launch the System Events application for the target user
88    err = noErr;
89    *path = '\0';
90
91    // LSCopyApplicationURLsForBundleIdentifier is not available before OS 10.10
92    CFArrayRef (*LSCopyAppURLsForBundleID)(CFStringRef, CFErrorRef) = NULL;
93    void *LSlib = dlopen("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/LaunchServices", RTLD_NOW | RTLD_NODELETE);
94    if (LSlib) {
95        LSCopyAppURLsForBundleID = (CFArrayRef(*)(CFStringRef, CFErrorRef)) dlsym(LSlib, "LSCopyApplicationURLsForBundleIdentifier");
96    }
97    if (LSCopyAppURLsForBundleID == NULL) {
98        err = fnfErr;
99    }
100
101#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101000
102    if (err != noErr) {     // LSCopyAppURLsForBundleID == NULL
103        //LSFindApplicationForInfo is deprecated in OS 10.10, so may not be available in the future
104        OSStatus (*LSFindAppForInfo)(OSType, CFStringRef, CFStringRef, FSRef*, CFURLRef*) = NULL;
105        if (LSlib) {
106            LSFindAppForInfo = (OSStatus(*)(OSType, CFStringRef, CFStringRef, FSRef*, CFURLRef*))
107                        dlsym(LSlib, "LSFindApplicationForInfo");
108        }
109        if (LSFindAppForInfo == NULL) {
110            if (LSlib) dlclose(LSlib);
111            return fnfErr;
112        }
113        err = (*LSFindAppForInfo)(creator, bundleID, NULL, NULL, &appURL);
114    } else  // if (LSCopyApplicationURLsForBundleIdentifier != NULL)
115#endif
116    {
117        if (err == noErr) {
118            CFArrayRef appRefs = (*LSCopyAppURLsForBundleID)(bundleID, NULL);
119            if (appRefs == NULL) {
120                err = fnfErr;
121            } else {
122                appURL = (CFURLRef)CFArrayGetValueAtIndex(appRefs, 0);
123                CFRetain(appURL);
124                CFRelease(appRefs);
125            }
126        }
127        if (err != noErr) {
128            if (LSlib) dlclose(LSlib);
129            return err;
130        }
131    }   // end if (LSCopyApplicationURLsForBundleIdentifier != NULL)
132
133    if (err == noErr) {
134        CFStringRef CFPath = CFURLCopyFileSystemPath(appURL, kCFURLPOSIXPathStyle);
135        CFStringGetCString(CFPath, path, maxLen, kCFStringEncodingUTF8);
136        CFRelease(CFPath);
137    }
138    if (appURL) {
139        CFRelease(appURL);
140    }
141    if (LSlib) dlclose(LSlib);
142    return err;
143}
144
145// Test OS version number on all versions of OS X without using deprecated Gestalt
146// compareOSVersionTo(x, y) returns:
147// -1 if the OS version we are running on is less than x.y
148//  0 if the OS version we are running on is equal to x.y
149// +1 if the OS version we are running on is lgreater than x.y
150int compareOSVersionTo(int toMajor, int toMinor) {
151    static SInt32 major = -1;
152    static SInt32 minor = -1;
153
154    if (major < 0) {
155        char vers[100], *p1 = NULL;
156        FILE *f;
157        vers[0] = '\0';
158        f = popen("sw_vers -productVersion", "r");
159        if (f) {
160            fscanf(f, "%s", vers);
161            pclose(f);
162        }
163        if (vers[0] == '\0') {
164            fprintf(stderr, "popen(\"sw_vers -productVersion\" failed\n");
165            fflush(stderr);
166            return 0;
167        }
168        // Extract the major system version number
169        major = atoi(vers);
170        // Extract the minor system version number
171        p1 = strchr(vers, '.');
172        minor = atoi(p1+1);
173    }
174
175    if (major < toMajor) return -1;
176    if (major > toMajor) return 1;
177    // if (major == toMajor) compare minor version numbers
178    if (minor < toMinor) return -1;
179    if (minor > toMinor) return 1;
180    return 0;
181}
182