14 #import <AppKit/AppKit.h>
15 #import <IOKit/pwr_mgt/IOPMLib.h>
16 #import <IOKit/pwr_mgt/IOPM.h>
18 #include <objc/objc-runtime.h>
29 static void **mainThread;
30 static dispatch_once_t once = 0;
31 dispatch_once(&once, ^{
32 mainThread = (
void **)dlsym(RTLD_SELF,
"VuoApp_mainThread");
34 mainThread = (
void **)dlsym(RTLD_DEFAULT,
"VuoApp_mainThread");
38 VUserLog(
"Error: Couldn't find VuoApp_mainThread.");
45 VUserLog(
"Error: VuoApp_mainThread isn't set.");
51 return *mainThread == (
void *)pthread_self();
75 VUserLog(
"Error: VuoEventLoop_processEvent must be called from the main thread.");
80 NSAutoreleasePool *pool = [NSAutoreleasePool new];
83 id *nsAppGlobal = (
id *)dlsym(RTLD_DEFAULT,
"NSApp");
86 if (nsAppGlobal && *nsAppGlobal)
93 NSEvent *
event = [*nsAppGlobal nextEventMatchingMask:NSEventMaskAny
94 untilDate:(mode == VuoEventLoop_WaitIndefinitely ? [NSDate distantFuture] : [NSDate distantPast])
95 inMode:NSDefaultRunLoopMode
97 [*nsAppGlobal sendEvent:event];
98 [*nsAppGlobal updateWindows];
104 if (mode == VuoEventLoop_WaitIndefinitely)
107 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0,
false);
121 id *nsAppGlobal = (
id *)dlsym(RTLD_DEFAULT,
"NSApp");
123 if (nsAppGlobal && *nsAppGlobal)
127 dispatch_async(dispatch_get_main_queue(), ^{
128 NSEvent *killswitch = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
129 location:NSMakePoint(0,0)
137 [*nsAppGlobal postEvent:killswitch atStart:NO];
143 CFRunLoopStop(CFRunLoopGetMain());
155 CFRunLoopStop(CFRunLoopGetMain());
167 VUserLog(
"Error: VuoEventLoop_mayBeTerminated must be called from the main thread.");
173 id *nsAppGlobal = (
id *)dlsym(RTLD_DEFAULT,
"NSApp");
174 if (!nsAppGlobal || !*nsAppGlobal)
177 if ([*nsAppGlobal modalWindow])
180 for (NSWindow *window in [*nsAppGlobal windows])
181 if ([window attachedSheet])
197 static dispatch_queue_attr_t attr = 0;
198 static dispatch_once_t once = 0;
199 dispatch_once(&once, ^{
200 attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0);
213 struct rlimit rl = {OPEN_MAX, OPEN_MAX};
214 getrlimit(RLIMIT_NOFILE, &rl);
215 rl.rlim_cur =
MIN(OPEN_MAX, rl.rlim_max);
216 if (setrlimit(RLIMIT_NOFILE, &rl))
217 VUserLog(
"Warning: Couldn't set open-files limit: %s", strerror(errno));
228 vuoStopCompositionType
vuoStopComposition = (vuoStopCompositionType)dlsym(RTLD_SELF,
"vuoStopComposition");
230 vuoStopComposition = (vuoStopCompositionType)dlsym(RTLD_DEFAULT,
"vuoStopComposition");
233 VUserLog(
"Warning: Couldn't find vuoStopComposition symbol; not installing clean-shutdown signal handlers.");
238 signal(SIGINT, SIG_IGN);
239 signal(SIGQUIT, SIG_IGN);
240 signal(SIGTERM, SIG_IGN);
244 void (^stop)(void) = ^{
247 dispatch_source_t sigintSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGINT, 0, dispatch_get_main_queue());
248 dispatch_source_t sigquitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGQUIT, 0, dispatch_get_main_queue());
249 dispatch_source_t sigtermSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, dispatch_get_main_queue());
250 dispatch_source_set_event_handler(sigintSource, stop);
251 dispatch_source_set_event_handler(sigquitSource, stop);
252 dispatch_source_set_event_handler(sigtermSource, stop);
253 dispatch_resume(sigintSource);
254 dispatch_resume(sigquitSource);
255 dispatch_resume(sigtermSource);
263 if (messageType != kIOPMMessageSystemPowerEventOccurred)
267 IOPMCopyCPUPowerStatus(&d);
268 CFNumberRef cpuSpeedLimitCF = CFDictionaryGetValue(d, CFSTR(kIOPMCPUPowerLimitProcessorSpeedKey));
269 int cpuSpeedLimit = -1;
270 CFNumberGetValue(cpuSpeedLimitCF, kCFNumberIntType, &cpuSpeedLimit);
273 if (cpuSpeedLimit >= 0)
274 VDebugLog(
"The system changed the CPU speed limit to %d%%.", cpuSpeedLimit);
283 void (^logThermalState)(
int thermalState) = ^(
int thermalState){
284 if (thermalState == 0)
286 else if (thermalState == 1)
287 VDebugLog(
"thermalState = fair (\"fans audible\")");
288 else if (thermalState == 2)
289 VDebugLog(
"thermalState = serious (\"fans at maximum speed\")");
290 else if (thermalState == 3)
291 VDebugLog(
"thermalState = critical (\"system needs to cool down\")");
293 if ([NSProcessInfo.processInfo respondsToSelector:
@selector(thermalState)])
295 logThermalState(((
int (*)(
id,
SEL))objc_msgSend)(NSProcessInfo.processInfo,
@selector(thermalState)));
296 [NSNotificationCenter.defaultCenter addObserverForName:@"NSProcessInfoThermalStateDidChangeNotification" object:nil queue:nil usingBlock:^(NSNotification *note){
297 logThermalState(((int (*)(id, SEL))objc_msgSend)(note.object, @selector(thermalState)));
303 io_service_t rootDomain = IORegistryEntryFromPath(kIOMasterPortDefault, kIOPowerPlane
":/IOPowerConnection/IOPMrootDomain");
304 IONotificationPortRef notePort = IONotificationPortCreate(MACH_PORT_NULL);
305 if (rootDomain && notePort)
307 io_object_t notification_object = MACH_PORT_NULL;
308 IOReturn ret = IOServiceAddInterestNotification(notePort, rootDomain, kIOGeneralInterest,
VuoShowSystemPowerEvent, NULL, ¬ification_object);
309 if (ret == kIOReturnSuccess)
311 CFRunLoopSourceRef runLoopSrc = IONotificationPortGetRunLoopSource(notePort);
313 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSrc, kCFRunLoopDefaultMode);
324 dispatch_source_t memoryPressureWatcher = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, DISPATCH_MEMORYPRESSURE_NORMAL|DISPATCH_MEMORYPRESSURE_WARN|DISPATCH_MEMORYPRESSURE_CRITICAL, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
325 dispatch_source_set_event_handler(memoryPressureWatcher, ^{
326 int pressure = dispatch_source_get_data(memoryPressureWatcher);
327 if (pressure == DISPATCH_MEMORYPRESSURE_NORMAL)
329 else if (pressure == DISPATCH_MEMORYPRESSURE_WARN)
331 else if (pressure == DISPATCH_MEMORYPRESSURE_CRITICAL)
334 dispatch_resume(memoryPressureWatcher);
342 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceWillSleepNotification object:nil queue:nil usingBlock:^(NSNotification *note){
343 VDebugLog("The system is going to sleep.");
344 VuoEventLoop_systemAsleep = true;
346 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceDidWakeNotification object:nil queue:nil usingBlock:^(NSNotification *note){
347 VDebugLog("The system is waking up.");
348 VuoEventLoop_systemAsleep = false;
350 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceWillPowerOffNotification object:nil queue:nil usingBlock:^(NSNotification *note){
351 VDebugLog("The system is powering off");
353 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceScreensDidSleepNotification object:nil queue:nil usingBlock:^(NSNotification *note){
354 VDebugLog("The screens are going to sleep.");
356 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceScreensDidWakeNotification object:nil queue:nil usingBlock:^(NSNotification *note){
357 VDebugLog("The screens are waking up.");
359 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note){
360 VDebugLog("The system is switching back to this user account.");
362 [NSWorkspace.sharedWorkspace.notificationCenter addObserverForName:NSWorkspaceSessionDidResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note){
363 VDebugLog("The system is switching to another user account.");
365 [NSNotificationCenter.defaultCenter addObserverForName:NSSystemClockDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note){
366 VDebugLog("The system's clock changed to %s", NSDate.date.description.UTF8String);
403 id activityToken = [NSProcessInfo.processInfo beginActivityWithOptions:NSActivityUserInitiated & ~NSActivitySuddenTerminationDisabled
404 reason: @"Many Vuo compositions need to process input and send output even when the app's window is not visible."];
406 [activityToken retain];