Vuo  2.0.0
VuoEventLoop.m
Go to the documentation of this file.
1 
10 #include "VuoEventLoop.h"
11 #include "VuoLog.h"
12 #include "VuoCompositionState.h"
13 
14 #ifndef NS_RETURNS_INNER_POINTER
15 #define NS_RETURNS_INNER_POINTER
16 #endif
17 #import <AppKit/AppKit.h>
18 
19 #include <dlfcn.h>
20 
39 {
40  NSAutoreleasePool *pool = [NSAutoreleasePool new];
41 
42  // Can't just use `NSApp` directly, since the composition might not link to AppKit.
43  id *nsAppGlobal = (id *)dlsym(RTLD_DEFAULT, "NSApp");
44 
45  // Support running either with or without an NSApplication.
46  if (nsAppGlobal && *nsAppGlobal)
47  {
48  // http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
49  // https://stackoverflow.com/questions/6732400/cocoa-integrate-nsapplication-into-an-existing-c-mainloop
50 
51  // When the composition is ready to stop, it posts a killswitch NSEvent,
52  // to ensure that this function returns immediately.
53  NSEvent *event = [*nsAppGlobal nextEventMatchingMask:NSAnyEventMask
54  untilDate:(mode == VuoEventLoop_WaitIndefinitely ? [NSDate distantFuture] : [NSDate distantPast])
55  inMode:NSDefaultRunLoopMode
56  dequeue:YES];
57  [*nsAppGlobal sendEvent:event];
58  [*nsAppGlobal updateWindows];
59  }
60  else
61  {
62  // This blocks until CFRunLoopStop() is called.
63  // When the composition is ready to stop (or switch into NSApplication mode), it calls CFRunLoopStop().
64  if (mode == VuoEventLoop_WaitIndefinitely)
65  CFRunLoopRun();
66  else
67  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
68  }
69 
70  [pool drain];
71 }
72 
77 {
78  // Can't just use `NSApp` directly, since the composition might not link to AppKit.
79  id *nsAppGlobal = (id *)dlsym(RTLD_DEFAULT, "NSApp");
80 
81  if (nsAppGlobal && *nsAppGlobal)
82  {
83  // Send an event, to ensure VuoEventLoop_processEvent()'s call to `nextEventMatchingMask:…` returns immediately.
84  NSEvent *killswitch = [NSEvent otherEventWithType:NSApplicationDefined
85  location:NSMakePoint(0,0)
86  modifierFlags:0
87  timestamp:0
88  windowNumber:0
89  context:nil
90  subtype:0
91  data1:0
92  data2:0];
93  [*nsAppGlobal postEvent:killswitch atStart:NO];
94  }
95  else
96  CFRunLoopStop(CFRunLoopGetMain());
97 }
98 
103 {
104  CFRunLoopStop(CFRunLoopGetMain());
105 }
106 
111 {
112  // Can't just use `NSApp` directly, since the composition might not link to AppKit.
113  id *nsAppGlobal = (id *)dlsym(RTLD_DEFAULT, "NSApp");
114  if (!nsAppGlobal || !*nsAppGlobal)
115  return true;
116 
117  if ([*nsAppGlobal modalWindow])
118  return false;
119 
120  for (NSWindow *window in [*nsAppGlobal windows])
121  if ([window attachedSheet])
122  return false;
123 
124  return true;
125 }
126 
134 {
135  static dispatch_queue_attr_t attr = 0;
136  static dispatch_once_t once = 0;
137  dispatch_once(&once, ^{
138  attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0);
139  });
140  return attr;
141 }
142 
147 {
148  typedef void (*vuoStopCompositionType)(struct VuoCompositionState *);
149  vuoStopCompositionType vuoStopComposition = (vuoStopCompositionType)dlsym(RTLD_SELF, "vuoStopComposition");
150  if (!vuoStopComposition)
151  vuoStopComposition = (vuoStopCompositionType)dlsym(RTLD_DEFAULT, "vuoStopComposition");
152  if (!vuoStopComposition)
153  {
154  VUserLog("Warning: Couldn't find vuoStopComposition symbol; not installing clean-shutdown signal handlers.");
155  return;
156  }
157 
158  // Disable default signal handlers so libdispatch can catch them.
159  signal(SIGINT, SIG_IGN);
160  signal(SIGQUIT, SIG_IGN);
161  signal(SIGTERM, SIG_IGN);
162 
163  // Use libdispatch to handle signals instead of `signal`/`sigaction`
164  // since `vuoStopComposition()` uses non-signal-safe functions such as `malloc`.
165  void (^stop)(void) = ^{
166  vuoStopComposition(NULL);
167  };
168  dispatch_source_t sigintSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGINT, 0, dispatch_get_main_queue());
169  dispatch_source_t sigquitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGQUIT, 0, dispatch_get_main_queue());
170  dispatch_source_t sigtermSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, dispatch_get_main_queue());
171  dispatch_source_set_event_handler(sigintSource, stop);
172  dispatch_source_set_event_handler(sigquitSource, stop);
173  dispatch_source_set_event_handler(sigtermSource, stop);
174  dispatch_resume(sigintSource);
175  dispatch_resume(sigquitSource);
176  dispatch_resume(sigtermSource);
177 }
178 
185 {
186  id activityToken = [[NSProcessInfo processInfo] performSelector:@selector(beginActivityWithOptions:reason:)
187  withObject: (id)((0x00FFFFFFULL | (1ULL << 20)) & ~(1ULL << 14)) // NSActivityUserInitiated & ~NSActivitySuddenTerminationDisabled
188  withObject: @"Many Vuo compositions need to process input and send output even when the app's window is not visible."];
189 
190  [activityToken retain];
191 }