Vuo 2.4.4
Loading...
Searching...
No Matches
VuoFileUtilitiesCocoa.mm
Go to the documentation of this file.
1
10#include "VuoFileUtilities.hh"
12#include "VuoException.hh"
13#include "VuoStringUtilities.hh"
14
15#include <AppKit/AppKit.h>
16#include <sys/utsname.h>
17
24{
25 NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:filePath.c_str()]];
26 if (!url)
27 throw VuoException("Couldn't move file '" + filePath + "' to the trash: Couldn't create NSURL.");
28
29 NSError *error = nil;
30 bool success = [[NSFileManager defaultManager] trashItemAtURL:url resultingItemURL:nil error:&error];
31 if (!success)
32 throw VuoException(string("Couldn't move file '" + filePath + "' to the trash: ") + [[error localizedDescription] UTF8String]);
33}
34
46{
47 NSString *pathNS = [NSString stringWithUTF8String:path.c_str()];
48 if (!pathNS)
49 throw VuoException("Path \"" + path + "\" isn't a valid UTF-8 string.");
50
51 NSURL *baseURL = [NSURL fileURLWithPath:pathNS isDirectory:YES];
52 if (!baseURL)
53 throw VuoException("Path \"" + path + "\" isn't a valid directory.");
54
55 NSURL *temporaryDirectoryURL = nil;
56 while (true)
57 {
58 NSError *error = nil;
59 temporaryDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory
60 inDomain:NSUserDomainMask
61 appropriateForURL:baseURL
62 create:YES
63 error:&error];
64 if (error)
65 {
66 NSError *underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
67 if ([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain]
68 && [underlyingError code] == ENOENT)
69 {
70 // File not found; go up a directory and try again.
71 baseURL = [baseURL URLByDeletingLastPathComponent];
72 }
73 else
74 throw VuoException((string("Couldn't get a temp folder: ") + [[error localizedDescription] UTF8String]).c_str());
75 }
76 else if (!temporaryDirectoryURL)
77 throw VuoException("Couldn't get a temp folder for path \"" + path + "\".");
78 else
79 break;
80 }
81
82 return [[temporaryDirectoryURL path] UTF8String];
83}
84
93{
94 NSString *pathNS = [NSString stringWithUTF8String:path.c_str()];
95 if (!pathNS)
96 throw VuoException("Path \"" + path + "\" isn't a valid UTF-8 string.");
97
98 NSDictionary *fileAttributes;
99 while (true)
100 {
101 NSError *error = nil;
102 fileAttributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:pathNS error:&error];
103 if (error)
104 {
105 NSError *underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
106 if ([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain]
107 && [underlyingError code] == ENOENT)
108 {
109 // File not found; go up a directory and try again.
110 pathNS = [pathNS stringByDeletingLastPathComponent];
111 }
112 else
113 throw VuoException((string("Couldn't get information about path: ") + [[error localizedDescription] UTF8String]).c_str());
114 }
115 else if (!fileAttributes)
116 throw VuoException("Couldn't get information about path \"" + path + "\".");
117 else
118 break;
119 }
120
121 unsigned long long freeSpace = [fileAttributes[NSFileSystemFreeSize] longLongValue];
122 return freeSpace;
123}
124
130void VuoFileUtilitiesCocoa_focusProcess(pid_t pid, bool force)
131{
132 if ([NSApp respondsToSelector:@selector(yieldActivationToApplication:)]) // macOS 14+
133 {
134 NSRunningApplication *compositionApp = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
135 [NSApp performSelector:@selector(yieldActivationToApplication:) withObject:compositionApp];
136 }
137 else
138 {
139 // On macOS 13, `-[NSRunningApplication activateWithOptions:]` often fails,
140 // but the older `SetFrontProcess()` API works, so use that instead.
141
142 // NSRunningApplication *compositionApp = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
143 // BOOL ret = [compositionApp activateWithOptions: NSApplicationActivateAllWindows
144 // | (force ? NSApplicationActivateIgnoringOtherApps : 0)];
145 // if (!ret)
146 // VUserLog("-[NSRunningApplication activateWithOptions:] failed for pid %d", pid);
147
148#pragma clang diagnostic push
149#pragma clang diagnostic ignored "-Wdeprecated-declarations"
150 ProcessSerialNumber psn;
151 OSErr ret = GetProcessForPID(pid, &psn);
152 if (ret != noErr)
153 {
154 VUserLog("GetProcessForPID(%d) failed: %d", pid, ret);
155 return;
156 }
157
158 ret = SetFrontProcess(&psn);
159 if (ret != noErr)
160 {
161 VUserLog("SetFrontProcess(%d) failed: %d", pid, ret);
162 return;
163 }
164#pragma clang diagnostic pop
165 }
166}
167
172{
173 // NSProcessInfo.processInfo.operatingSystemVersion.majorVersion returns 10.16
174 // (instead of the actual version) on macOS versions newer than 10.15,
175 // since Vuo uses an older macOS SDK for compatibility with older macOS versions.
176 // This prevents distinguishing between e.g., macOS 12 and 13.
177 // However, NSProcessInfo.processInfo.operatingSystemVersionString returns the actual version,
178 // so try to extract it from there.
179
180 int majorVersion = -1;
181 sscanf(NSProcessInfo.processInfo.operatingSystemVersionString.UTF8String, "Version %d", &majorVersion);
182
183 if (majorVersion != -1)
184 return majorVersion;
185 else
186 return NSProcessInfo.processInfo.operatingSystemVersion.majorVersion;
187}
188
193{
194 // See VuoFileUtilitiesCocoa_getOSVersionMajor.
195
196 int majorVersion = -1;
197 int minorVersion = -1;
198 sscanf(NSProcessInfo.processInfo.operatingSystemVersionString.UTF8String, "Version %d.%d", &majorVersion, &minorVersion);
199
200 if (majorVersion != -1 && minorVersion != -1)
201 return minorVersion;
202 else
203 return NSProcessInfo.processInfo.operatingSystemVersion.minorVersion;
204}
205
210{
211 struct utsname un;
212 if (uname(&un) < 0)
213 throw VuoException("Couldn't look up the system's architecture: %s", strerror(errno));
214
215 return un.machine;
216}
217
223{
224 CFStringRef path = CFStringCreateWithCString(NULL, filePath.c_str(), kCFStringEncodingUTF8);
225 CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true);
226 CFErrorRef error;
227 if (!CFURLSetResourcePropertyForKey(url, kCFURLIsPackageKey, kCFBooleanTrue, &error))
228 {
229 CFStringRef errorString = CFErrorCopyDescription(error);
230 VUserLog("Warning: Couldn't set kCFURLIsPackageKey on this bundle: %s", VuoStringUtilities::makeFromCFString(errorString).c_str());
231 CFRelease(errorString);
232 }
233 CFRelease(url);
234 CFRelease(path);
235}
236
240static CFURLRef VuoFileUtilitiesCocoa_getCFURL(const string &path)
241{
242 if (path.empty())
243 return nullptr;
244
245 CFStringRef cfpath = CFStringCreateWithCString(nullptr, path.c_str(), kCFStringEncodingUTF8);
246 if (!cfpath)
247 throw VuoException("Path \"" + path + "\" isn't a valid UTF-8 string.");
248 VuoDefer(^{ CFRelease(cfpath); });
249
250 CFURLRef url = CFURLCreateWithFileSystemPath(nullptr, cfpath, kCFURLPOSIXPathStyle, true);
251 if (!url)
252 throw VuoException("Path \"" + path + "\" isn't a valid POSIX path.");
253
254 return url;
255}
256
260static CFDataRef VuoFileUtilitiesCocoa_getMacAliasData(const string &path)
261{
262 CFURLRef url = VuoFileUtilitiesCocoa_getCFURL(path);
263 if (!url)
264 return nullptr;
265 VuoDefer(^{ CFRelease(url); });
266
267 return CFURLCreateBookmarkDataFromFile(nullptr, url, nullptr);
268}
269
275bool VuoFileUtilitiesCocoa_isMacAlias(const string &path)
276{
277 CFDataRef aliasData = VuoFileUtilitiesCocoa_getMacAliasData(path);
278 if (!aliasData)
279 return false;
280
281 CFRelease(aliasData);
282 return true;
283}
284
291{
292 CFDataRef aliasData = VuoFileUtilitiesCocoa_getMacAliasData(path);
293 if (!aliasData)
294 throw VuoException("Path \"" + path + "\" isn't a macOS Alias.");
295 VuoDefer(^{ CFRelease(aliasData); });
296
297 CFURLRef url = VuoFileUtilitiesCocoa_getCFURL(path);
298 VuoDefer(^{ CFRelease(url); });
299
300 Boolean isStale = false;
301 CFErrorRef error = nullptr;
302 CFURLRef resolvedURL = CFURLCreateByResolvingBookmarkData(nullptr, aliasData, kCFBookmarkResolutionWithoutUIMask, url, nullptr, &isStale, &error);
303 if (!resolvedURL)
304 {
305 CFStringRef desc = CFErrorCopyDescription(error);
306 VuoDefer(^{ CFRelease(desc); });
307 throw VuoException("Path \"" + path + "\" is a macOS Alias, but it couldn't be resolved: \"" + VuoStringUtilities::makeFromCFString(desc) + "\" (isStale=" + std::to_string(isStale) + ")");
308 }
309 VuoDefer(^{ CFRelease(resolvedURL); });
310
311 CFStringRef resolvedPath = CFURLCopyFileSystemPath(resolvedURL, kCFURLPOSIXPathStyle);
312 if (!resolvedPath)
313 throw VuoException("Path \"" + path + "\" is a macOS Alias, but it resolved to something that isn't a POSIX path (\"" + VuoStringUtilities::makeFromCFString(((NSURL *)resolvedURL).description) + "\").");
314 VuoDefer(^{ CFRelease(resolvedPath); });
315
316 return VuoStringUtilities::makeFromCFString(resolvedPath);
317}