Vuo 2.4.4
Loading...
Searching...
No Matches
VuoApp.m
Go to the documentation of this file.
1
10#include "VuoApp.h"
11
13#import <AppKit/AppKit.h>
14
15#include <dlfcn.h>
16#include <pthread.h>
17#include <libproc.h>
18#include <mach-o/dyld.h>
19#import <libgen.h>
20#import <dirent.h>
21
22#include "VuoCompositionState.h"
23#include "VuoEventLoop.h"
24#import "VuoAppDelegate.h"
26
31const double VuoApp_windowFadeSeconds = 0.45;
32
37{
38 static void **VuoApp_mainThread;
39 static dispatch_once_t once = 0;
40 dispatch_once(&once, ^{
41 VuoApp_mainThread = (void **)dlsym(RTLD_SELF, "VuoApp_mainThread");
43 VuoApp_mainThread = (void **)dlsym(RTLD_DEFAULT, "VuoApp_mainThread");
44
46 {
47 VUserLog("Error: Couldn't find VuoApp_mainThread.");
49 exit(1);
50 }
51
53 {
54 VUserLog("Error: VuoApp_mainThread isn't set.");
56 exit(1);
57 }
58 });
59
60 return *VuoApp_mainThread == (void *)pthread_self();
61}
62
72void VuoApp_executeOnMainThread(void (^block)(void))
73{
74 if (!block)
75 return;
76
78 block();
79 else
80 {
81 VUOLOG_PROFILE_BEGIN(mainQueue);
82 dispatch_sync(dispatch_get_main_queue(), ^{
83 VUOLOG_PROFILE_END(mainQueue);
84 block();
85 });
86 }
87}
88
110char *VuoApp_getName(void)
111{
112 char **dylibPath = (char **)dlsym(RTLD_SELF, "VuoApp_dylibPath");
113 if (!dylibPath)
114 dylibPath = (char **)dlsym(RTLD_DEFAULT, "VuoApp_dylibPath");
115 if (dylibPath)
116 {
117 char *filename = strrchr(*dylibPath, '/');
118 if (filename)
119 {
120 char *name = strdup(filename + 1); // Trim leading slash.
121 name[strlen(name) - strlen("-XXXXXX.dylib")] = 0;
122
123 if (strcmp(name, "VuoComposition") == 0)
124 {
125 free(name);
126 return strdup("Vuo Composition");
127 }
128
129 return name;
130 }
131 }
132
133 pid_t runnerPid = VuoGetRunnerPid();
134 if (runnerPid > 0)
135 {
136 char *runnerName = (char *)malloc(2*MAXCOMLEN);
137 proc_name(runnerPid, runnerName, 2*MAXCOMLEN);
138 return runnerName;
139 }
140
141 NSBundle *mainBundle = [NSBundle mainBundle];
142 NSString *name = [mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
143 if (!name)
144 name = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"];
145 if (!name)
146 name = [mainBundle objectForInfoDictionaryKey:@"CFBundleExecutable"];
147 if (!name)
148 name = [[[mainBundle executablePath] stringByDeletingPathExtension] lastPathComponent];
149
150 if (name)
151 return strdup([name UTF8String]);
152 else
153 return strdup("");
154}
155
161static void VuoApp_initNSApplication(bool requiresDockIcon)
162{
163 NSAutoreleasePool *pool = [NSAutoreleasePool new];
164
165 // https://stackoverflow.com/a/11010614/238387
166 NSApplication *app = [NSApplication sharedApplication];
167
168 if (![app delegate])
169 [app setDelegate:[VuoAppDelegate new]];
170
171 // When there's no host app present,
172 // create the default menu with the About and Quit menu items,
173 // to be overridden if/when any windows get focus.
175
176 BOOL ret = [app setActivationPolicy:requiresDockIcon ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory];
177 if (!ret)
178 VUserLog("-[NSApplication setActivationPolicy:%d] failed", requiresDockIcon);
179
180 // Stop bouncing in the dock.
181 [app finishLaunching];
182
184
185 pid_t runnerPid = VuoGetRunnerPid();
186 if (runnerPid > 1
187 && [NSRunningApplication.currentApplication respondsToSelector:@selector(activateFromApplication:options:)])
188 {
189 NSRunningApplication *runnerApp = [NSRunningApplication runningApplicationWithProcessIdentifier:runnerPid];
190 BOOL activated = (BOOL)[NSRunningApplication.currentApplication
191 performSelector:@selector(activateFromApplication:options:)
192 withObject:runnerApp
193 withObject:(id)NSApplicationActivateAllWindows];
194 if (!activated)
195 VUserLog("-[NSRunningApplication activateFromApplication:%d options:] failed", runnerPid);
196 }
197
200
201 [pool drain];
202}
203
207static bool VuoApp_initialized = false;
208
225void VuoApp_init(bool requiresDockIcon)
226{
227 if (NSApp)
228 {
230 if (requiresDockIcon
231 && NSApplication.sharedApplication.activationPolicy != NSApplicationActivationPolicyRegular)
232 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
233 });
234 return;
235 }
236
237 static dispatch_once_t once = 0;
238 dispatch_once(&once, ^{
239 VuoApp_initialized = true;
240
242 VuoApp_initNSApplication(requiresDockIcon);
243 });
244 });
245}
246
252static void VuoApp_finiWindows(uint64_t compositionUid)
253{
254 SEL stopRecording = @selector(stopRecording);
255 SEL compositionUidSel = @selector(compositionUid);
256 for (NSWindow *window in [NSApp windows])
257 // Stop any window recordings currently in progress.
258 // This prompts the user for the save destination,
259 // so make sure these complete before shutting the composition down.
260 if ([window respondsToSelector:stopRecording]
261 && [window respondsToSelector:compositionUidSel]
262 && (uint64_t)[window performSelector:compositionUidSel] == compositionUid)
263 [window performSelector:stopRecording];
264
265 if ([NSApp windows].count)
266 {
267 // Animate removing the app from the dock while the window is fading out (instead of waiting until after).
268 [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
269
270 double fudge = .1; // Wait a little longer, to ensure the animation's completionHandler gets called.
271 [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:VuoApp_windowFadeSeconds + fudge]];
272 }
273
274 // Avoid leaving menubar remnants behind.
275 // https://b33p.net/kosada/node/13384
276 [NSApp hide:nil];
277}
278
286void VuoApp_fini(void)
287{
288 if (! VuoApp_initialized)
289 return;
290
291 static dispatch_once_t once = 0;
292 dispatch_once(&once, ^{
293 void *compositionState = vuoCopyCompositionStateFromThreadLocalStorage();
294 uint64_t compositionUid = vuoGetCompositionUniqueIdentifier(compositionState);
295 free(compositionState);
296
298 VuoApp_finiWindows(compositionUid);
299 else
300 {
301 VUOLOG_PROFILE_BEGIN(mainQueue);
302 dispatch_sync(dispatch_get_main_queue(), ^{
303 VUOLOG_PROFILE_END(mainQueue);
304 VuoApp_finiWindows(compositionUid);
305 });
306 }
307 });
308}