diff --git a/KompleteSynthesia/AppDelegate.m b/KompleteSynthesia/AppDelegate.m index 88a6370..633423f 100644 --- a/KompleteSynthesia/AppDelegate.m +++ b/KompleteSynthesia/AppDelegate.m @@ -26,13 +26,66 @@ @interface AppDelegate () @end -@implementation AppDelegate +enum { + kAlienHardwareAgent = 0, + kAlienHostIntegration, + kAlienDaemon, + kAlienItemCount +}; + +@implementation AppDelegate { + BOOL restartAlien[kAlienItemCount]; +} + ++ (BOOL)terminateApp:(NSString*)name +{ + NSArray* apps = [[NSWorkspace sharedWorkspace] runningApplications]; + for (NSRunningApplication* app in apps) { + if ([app.localizedName compare:name] == NSOrderedSame) { + NSLog(@"found and trying to kill..."); + return [app forceTerminate]; + } + } + return NO; +} + +NSString* kHardwareAgentName = @"NIHardwareAgent.app"; +NSString* kHardwareAgentPath = @"/Library/Application Support/Native Instruments/Hardware/NIHardwareAgent.app"; + +NSString* kHostIntegrationAgentName = @"NIHostIntegrationAgent.app"; +NSString* kHostIntegrationAgentPath = @"/Library/Application Support/Native Instruments/Hardware/NIHostIntegrationAgent.app"; + +NSString* kDaemonName = @"NIHardwareAgent.app"; +NSString* kDaemonPath = @"/Library/Application Support/Native Instruments/Hardware/NIHardwareAgent.app"; + +- (void)killNativeInstrumentsComponentsIfNeeded +{ + NSString* fmtAssert = @"asserting %@ not active"; + NSString* fmtStopped = @"stopped %@"; + NSArray* items = @[ kHardwareAgentName, kHostIntegrationAgentName, kDaemonName ]; + + assert(items.count == kAlienItemCount); + + for (int i = 0; i < kAlienItemCount; i++) { + [_logViewController logLine:[NSString stringWithFormat:fmtAssert, items[i]]]; + + restartAlien[i] = [AppDelegate terminateApp:items[i]]; + + if (restartAlien[i]) { + [_logViewController logLine:[NSString stringWithFormat:fmtStopped, items[i]]]; + } + } +} - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSError* error = nil; _logViewController = [[LogViewController alloc] initWithNibName:@"LogViewController" bundle:NULL]; + + [self killNativeInstrumentsComponentsIfNeeded]; + + [NSThread sleepForTimeInterval:0.5f]; _synthesia = [[SynthesiaController alloc] initWithLogViewController:_logViewController delegate:self @@ -90,6 +143,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification self.statusMenu = menu; if ([SynthesiaController synthesiaRunning] == NO) { + [_logViewController logLine:@"Synthesia not running, starting it now"]; [_midi2hidController boostrapSynthesia]; } } @@ -154,6 +208,16 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification { [_videoController stopMirroringAndWait:YES]; [_midi2hidController teardown]; + + NSArray* items = @[ kHardwareAgentPath, kHostIntegrationAgentPath, kDaemonPath ]; + + assert(items.count == kAlienItemCount); + + for (int i = 0; i < kAlienItemCount; i++) { + if (restartAlien[i]) { + system([NSString stringWithFormat:@"open '%@'", items[i]].cString); + } + } } - (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app diff --git a/KompleteSynthesia/KompleteSynthesia.entitlements b/KompleteSynthesia/KompleteSynthesia.entitlements index 649916d..5d1af32 100644 --- a/KompleteSynthesia/KompleteSynthesia.entitlements +++ b/KompleteSynthesia/KompleteSynthesia.entitlements @@ -3,10 +3,17 @@ com.apple.security.app-sandbox - + com.apple.security.device.usb + com.apple.security.get-task-allow + com.apple.security.files.user-selected.read-write + com.apple.security.temporary-exception.apple-events + + com.native-instruments.NIHostIntegrationAgent + com.native-instruments.NIHardwareService + diff --git a/KompleteSynthesia/SynthesiaController.h b/KompleteSynthesia/SynthesiaController.h index 66d61b7..a6e9b7b 100644 --- a/KompleteSynthesia/SynthesiaController.h +++ b/KompleteSynthesia/SynthesiaController.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) id delegate; ++ (BOOL)applicationIsRunning:(NSString*)name; + (BOOL)synthesiaRunning; + (BOOL)synthesiaHasFocus; + (BOOL)activateSynthesia; @@ -33,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN - (id)initWithLogViewController:(LogViewController*)logViewController delegate:(id)delegate error:(NSError**)error; - (BOOL)assertMultiDeviceConfig:(NSError**)error message:(NSString*_Nullable *_Nullable)message; -//- (void)adjustVolumeByDelta:(int)delta; + @end NS_ASSUME_NONNULL_END diff --git a/KompleteSynthesia/SynthesiaController.m b/KompleteSynthesia/SynthesiaController.m index 587e374..e0dec90 100644 --- a/KompleteSynthesia/SynthesiaController.m +++ b/KompleteSynthesia/SynthesiaController.m @@ -54,17 +54,22 @@ + (int)synthesiaWindowNumber return 0; } -+ (BOOL)synthesiaRunning ++ (BOOL)applicationIsRunning:(NSString*)name { NSArray* apps = [[NSWorkspace sharedWorkspace] runningApplications]; for (NSRunningApplication* app in apps) { - if ([app.localizedName compare:kSynthesiaApplicationName] == NSOrderedSame) { + if ([app.localizedName compare:name] == NSOrderedSame) { return YES; } } return NO; } ++ (BOOL)synthesiaRunning +{ + return [SynthesiaController applicationIsRunning:kSynthesiaApplicationName]; +} + + (void)runSynthesiaWithCompletion:(void(^)(void))completion { NSWorkspaceOpenConfiguration* configuration = [NSWorkspaceOpenConfiguration new]; diff --git a/KompleteSynthesia/VideoController.m b/KompleteSynthesia/VideoController.m index 09f1688..d5c5d89 100644 --- a/KompleteSynthesia/VideoController.m +++ b/KompleteSynthesia/VideoController.m @@ -42,11 +42,14 @@ - (id)initWithLogViewController:(LogViewController*)lc error:(NSError**)error screenBuffer[1] = NULL; atomic_fetch_and(&stopMirroring, 0); atomic_fetch_and(&mirrorActive, 0); + + log = lc; usb = [[USBController alloc] initWithError:error]; if (usb == nil) { return nil; } + [log logLine:[NSString stringWithFormat:@"detected %@ USB device", usb.deviceName]]; if (usb.mk2Controller == YES) { _screenCount = 2; @@ -93,11 +96,11 @@ - (BOOL)startMirroring int windowNumber = [SynthesiaController synthesiaWindowNumber]; if (windowNumber == 0) { - NSLog(@"Synthesia window not found"); + NSLog(@"synthesia window not found"); return NO; } - [log logLine:@"Starting window mirroring"]; + [log logLine:@"starting window mirroring"]; dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ atomic_fetch_or(&mirrorActive, 1); @@ -125,7 +128,7 @@ - (BOOL)startMirroring - (BOOL)reset:(NSError**)error { if ([SynthesiaController synthesiaRunning] == NO) { - [log logLine:@"We need synthesia running for grabbing its video"]; + [log logLine:@"we need synthesia running for grabbing its video"]; if (error != nil) { NSDictionary *userInfo = @{ diff --git a/README.md b/README.md index 2edf2b7..3bab143 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Native Instruments Komplete Kontrol Light Guide and Screen Mirroring Support for ## Features -Routes Synthesia lighting information to your Native Instruments keyboard controller USB device. +Routes Synthesia lighting information to your Native Instruments keyboard controller USB device. Mirrors the Synthesia application window onto your Komplete Kontrol MK2 displays. Auto-detects a Native Instruments S-series keyboard controller USB device. Listens on the 'LoopBe' MIDI input interface port. Notes received are forwarded to the keyboard controller USB device as key lighting requests adhering to the Synthesia protocol.