123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Unity Technologies.
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
- #import <Cocoa/Cocoa.h>
- #import <Foundation/Foundation.h>
-
- // 'FSnd' FourCC
- #define keyFileSender 1179872868
-
- // 16 bit aligned legacy struct - this should total 20 bytes
- typedef struct _SelectionRange
- {
- int16_t unused1; // 0 (not used)
- int16_t lineNum; // line to select (<0 to specify range)
- int32_t startRange; // start of selection range (if line < 0)
- int32_t endRange; // end of selection range (if line < 0)
- int32_t unused2; // 0 (not used)
- int32_t theDate; // modification date/time
- } __attribute__((packed)) SelectionRange;
-
- static NSString* MakeNSString(const char* str)
- {
- if (!str)
- return NULL;
-
- NSString* ret = [NSString stringWithUTF8String: str];
- return ret;
- }
-
- static UInt32 GetCreatorOfThisApp()
- {
- static UInt32 creator = 0;
- if (creator == 0)
- {
- UInt32 type;
- CFBundleGetPackageInfo(CFBundleGetMainBundle(), &type, &creator);
- }
- return creator;
- }
-
- static BOOL OpenFileAtLineWithAppleEvent(NSRunningApplication *runningApp, NSString* path, int line)
- {
- if (!runningApp)
- return NO;
-
- NSURL *pathUrl = [NSURL fileURLWithPath: path];
-
- NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor
- descriptorWithProcessIdentifier: runningApp.processIdentifier];
-
- NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor
- appleEventWithEventClass: kCoreEventClass
- eventID: kAEOpenDocuments
- targetDescriptor: targetDescriptor
- returnID: kAutoGenerateReturnID
- transactionID: kAnyTransactionID];
-
- [appleEvent
- setParamDescriptor: [NSAppleEventDescriptor
- descriptorWithDescriptorType: typeFileURL
- data: [[pathUrl absoluteString] dataUsingEncoding: NSUTF8StringEncoding]]
- forKeyword: keyDirectObject];
-
- UInt32 packageCreator = GetCreatorOfThisApp();
- if (packageCreator == kUnknownType) {
- [appleEvent
- setParamDescriptor: [NSAppleEventDescriptor
- descriptorWithDescriptorType: typeApplicationBundleID
- data: [[[NSBundle mainBundle] bundleIdentifier] dataUsingEncoding: NSUTF8StringEncoding]]
- forKeyword: keyFileSender];
- } else {
- [appleEvent
- setParamDescriptor: [NSAppleEventDescriptor descriptorWithTypeCode: packageCreator]
- forKeyword: keyFileSender];
- }
-
- if (line != -1) {
- // Add selection range to event
- SelectionRange range;
- range.unused1 = 0;
- range.lineNum = line - 1;
- range.startRange = -1;
- range.endRange = -1;
- range.unused2 = 0;
- range.theDate = -1;
-
- [appleEvent
- setParamDescriptor: [NSAppleEventDescriptor
- descriptorWithDescriptorType: typeChar
- bytes: &range
- length: sizeof(SelectionRange)]
- forKeyword: keyAEPosition];
- }
-
- AEDesc reply = { typeNull, NULL };
- OSErr err = AESendMessage(
- [appleEvent aeDesc],
- &reply,
- kAENoReply + kAENeverInteract,
- kAEDefaultTimeout);
-
- return err == noErr;
- }
-
- static BOOL ApplicationSupportsQueryOpenedSolution(NSString* appPath)
- {
- NSURL* appUrl = [NSURL fileURLWithPath: appPath];
- NSBundle* bundle = [NSBundle bundleWithURL: appUrl];
-
- if (!bundle)
- return NO;
-
- id versionValue = [bundle objectForInfoDictionaryKey: @"CFBundleVersion"];
- if (!versionValue || ![versionValue isKindOfClass: [NSString class]])
- return NO;
-
- NSString* version = (NSString*)versionValue;
- return [version compare:@"8.6" options:NSNumericSearch] != NSOrderedAscending;
- }
-
- static NSArray<NSRunningApplication*>* QueryRunningInstances(NSString *appPath)
- {
- NSMutableArray<NSRunningApplication*>* instances = [[NSMutableArray alloc] init];
- NSURL *appUrl = [NSURL fileURLWithPath: appPath];
-
- for (NSRunningApplication *runningApp in NSWorkspace.sharedWorkspace.runningApplications) {
- if (![runningApp isTerminated] && [runningApp.bundleURL isEqual: appUrl]) {
- [instances addObject: runningApp];
- }
- }
-
- return instances;
- }
-
- enum {
- kWorkspaceEventClass = 1448302419, /* 'VSWS' FourCC */
- kCurrentSelectedSolutionPathEventID = 1129534288 /* 'CSSP' FourCC */
- };
-
- static BOOL TryQueryCurrentSolutionPath(NSRunningApplication* runningApp, NSString** solutionPath)
- {
- NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor
- descriptorWithProcessIdentifier: runningApp.processIdentifier];
-
- NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor
- appleEventWithEventClass: kWorkspaceEventClass
- eventID: kCurrentSelectedSolutionPathEventID
- targetDescriptor: targetDescriptor
- returnID: kAutoGenerateReturnID
- transactionID: kAnyTransactionID];
-
- AEDesc aeReply = { 0, };
-
- OSErr sendResult = AESendMessage(
- [appleEvent aeDesc],
- &aeReply,
- kAEWaitReply | kAENeverInteract,
- kAEDefaultTimeout);
-
- if (sendResult != noErr) {
- return NO;
- }
-
- NSAppleEventDescriptor *reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy: &aeReply];
- *solutionPath = [[reply descriptorForKeyword: keyDirectObject] stringValue];
-
- return *solutionPath != NULL;
- }
-
- static NSRunningApplication* QueryRunningApplicationOpenedOnSolution(NSString* appPath, NSString* solutionPath)
- {
- BOOL supportsQueryOpenedSolution = ApplicationSupportsQueryOpenedSolution(appPath);
-
- for (NSRunningApplication *runningApp in QueryRunningInstances(appPath)) {
- // If the currently selected external editor does not support the opened solution apple event
- // then fallback to the previous behavior: take the first opened VSM and open the solution
- if (!supportsQueryOpenedSolution) {
- OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
- return runningApp;
- }
-
- NSString* currentSolutionPath;
- if (TryQueryCurrentSolutionPath(runningApp, ¤tSolutionPath)) {
- if ([solutionPath isEqual:currentSolutionPath]) {
- return runningApp;
- }
- } else {
- // If VSM doesn't respond to the query opened solution event
- // we fallback to the previous behavior too
- OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
- return runningApp;
- }
- }
-
- return NULL;
- }
-
- static NSRunningApplication* LaunchApplicationOnSolution(NSString* appPath, NSString* solutionPath)
- {
- return [[NSWorkspace sharedWorkspace]
- launchApplicationAtURL: [NSURL fileURLWithPath: appPath]
- options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
- configuration: @{
- NSWorkspaceLaunchConfigurationArguments: @[ solutionPath ],
- }
- error: nil];
- }
-
- static NSRunningApplication* QueryOrLaunchApplication(NSString* appPath, NSString* solutionPath)
- {
- NSRunningApplication* runningApp = QueryRunningApplicationOpenedOnSolution(appPath, solutionPath);
-
- if (!runningApp)
- runningApp = LaunchApplicationOnSolution(appPath, solutionPath);
-
- if (runningApp)
- [runningApp activateWithOptions: 0];
-
- return runningApp;
- }
-
- BOOL LaunchOrReuseApp(NSString* appPath, NSString* solutionPath, NSRunningApplication** outApp)
- {
- NSRunningApplication* app = QueryOrLaunchApplication(appPath, solutionPath);
-
- if (outApp)
- *outApp = app;
-
- return app != NULL;
- }
-
- BOOL MonoDevelopOpenFile(NSString* appPath, NSString* solutionPath, NSString* filePath, int line)
- {
- NSRunningApplication* runningApp;
- if (!LaunchOrReuseApp(appPath, solutionPath, &runningApp)) {
- return FALSE;
- }
-
- if (filePath) {
- return OpenFileAtLineWithAppleEvent(runningApp, filePath, line);
- }
-
- return YES;
- }
-
- #if BUILD_APP
-
- int main(int argc, const char** argv)
- {
- if (argc != 5) {
- printf("Usage: AppleEventIntegration appPath solutionPath filePath lineNumber\n");
- return 1;
- }
-
- const char* appPath = argv[1];
- const char* solutionPath = argv[2];
- const char* filePath = argv[3];
- const int lineNumber = atoi(argv[4]);
-
- @autoreleasepool
- {
- MonoDevelopOpenFile(MakeNSString(appPath), MakeNSString(solutionPath), MakeNSString(filePath), lineNumber);
- }
-
- return 0;
- }
-
- #else
-
- extern "C"
- {
- BOOL OpenVisualStudio(const char* appPath, const char* solutionPath, const char* filePath, int line)
- {
- return MonoDevelopOpenFile(MakeNSString(appPath), MakeNSString(solutionPath), MakeNSString(filePath), line);
- }
- }
-
- #endif
|