diff options
author | Aaron Bockover <abock@microsoft.com> | 2019-10-30 23:12:54 +0300 |
---|---|---|
committer | monojenkins <jo.shields+jenkins@xamarin.com> | 2019-11-05 03:19:57 +0300 |
commit | dec980560eac3ca0d870a47cd61410b227498bb8 (patch) | |
tree | 8bdb594c3a1ab5d2f4a0c173fb3c24c8f0eb5926 | |
parent | eb93b60a5eae416d0c0cbfc37c27892b8710e2c1 (diff) |
MacIdeAppleEvents: add an apple event for getting current solution pathbackport-pr-9172-to-release-8.4
-rw-r--r-- | main/src/addins/MacPlatform/MacIdeAppleEvents.cs | 86 | ||||
-rw-r--r-- | main/src/addins/MacPlatform/MacPlatform.cs | 4 | ||||
-rw-r--r-- | main/src/addins/MacPlatform/MacPlatform.csproj | 4 | ||||
-rw-r--r-- | main/src/addins/MacPlatform/query-vsmac.mm | 204 |
4 files changed, 298 insertions, 0 deletions
diff --git a/main/src/addins/MacPlatform/MacIdeAppleEvents.cs b/main/src/addins/MacPlatform/MacIdeAppleEvents.cs new file mode 100644 index 0000000000..2fecf12ecd --- /dev/null +++ b/main/src/addins/MacPlatform/MacIdeAppleEvents.cs @@ -0,0 +1,86 @@ +// +// Copyright (c) Microsoft Corp. (https://www.microsoft.com) +// +// Authors: +// Aaron Bockover <abock@microsoft.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using Foundation; +using ObjCRuntime; + +using MonoDevelop.Core; +using MonoDevelop.Ide; + +namespace MacPlatform +{ + sealed class MacIdeAppleEvents : NSObject + { + // From AE.framework AppleEvents.h + const uint keyDirectObject = 757935405; // '----' + const uint keyErrorNumber = 1701999214; // 'errn' + const uint keyErrorString = 1701999219; // 'errs' + + // Our own private event FourCCs; note that all-lowercase FourCCs are reserved by Apple; + // any FourCC that has at least one capital letter is considered private to the application. + const AEEventClass WorkspaceEventClass = (AEEventClass)1448302419; // 'VSWS' FourCC + const AEEventID CurrentSelectedSolutionPathEventID = (AEEventID)1129534288; // 'CSSP' FourCC + + public MacIdeAppleEvents () + { + NSAppleEventManager.SharedAppleEventManager.SetEventHandler ( + this, + sel_getCurrentSelectedSolutionPath_withReply_, + WorkspaceEventClass, + CurrentSelectedSolutionPathEventID); + } + + protected override void Dispose (bool disposing) + { + if (disposing) { + NSAppleEventManager.SharedAppleEventManager.RemoveEventHandler ( + WorkspaceEventClass, + CurrentSelectedSolutionPathEventID); + } + + base.Dispose (disposing); + } + + const string getCurrentSelectedSolutionPath_withReply_ + = "getCurrentSelectedSolutionPath:withReply:"; + + static readonly Selector sel_getCurrentSelectedSolutionPath_withReply_ + = new Selector (getCurrentSelectedSolutionPath_withReply_); + + [Export (getCurrentSelectedSolutionPath_withReply_)] + void GetCurrentSelectedSolutionPath (NSAppleEventDescriptor @event, NSAppleEventDescriptor reply) + { + LoggingService.LogInfo ($"{nameof (GetCurrentSelectedSolutionPath)}: received AppleEvent {@event}"); + + var solutionPath = IdeApp.Workspace.CurrentSelectedSolution?.FileName.FullPath.ToString (); + if (!string.IsNullOrEmpty (solutionPath)) { + reply.SetParamDescriptorforKeyword ( + NSAppleEventDescriptor.DescriptorWithString (solutionPath), + keyDirectObject); + + LoggingService.LogInfo ($"{nameof (GetCurrentSelectedSolutionPath)}: replying to AppleEvent {@event} with {solutionPath}"); + } + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MacPlatform/MacPlatform.cs b/main/src/addins/MacPlatform/MacPlatform.cs index 85e3d11f50..0034752c0e 100644 --- a/main/src/addins/MacPlatform/MacPlatform.cs +++ b/main/src/addins/MacPlatform/MacPlatform.cs @@ -139,6 +139,8 @@ namespace MonoDevelop.MacIntegration AccessibilityInUse = val.BoolValue; } + MacIdeAppleEvents ideAppleEvents; + public MacPlatformService () { if (initedGlobal) @@ -349,6 +351,8 @@ namespace MonoDevelop.MacIntegration IdeApp.LaunchReason = IdeApp.LaunchType.Normal; } + ideAppleEvents = new MacIdeAppleEvents (); + return loaded; } diff --git a/main/src/addins/MacPlatform/MacPlatform.csproj b/main/src/addins/MacPlatform/MacPlatform.csproj index 7f051bd92d..403ddb6edb 100644 --- a/main/src/addins/MacPlatform/MacPlatform.csproj +++ b/main/src/addins/MacPlatform/MacPlatform.csproj @@ -116,6 +116,7 @@ <Compile Include="MacHttpMessageHandlerProvider.cs" /> <Compile Include="NSUrlSessionCredentialsHandler.cs" /> <Compile Include="BasicAuthenticationHandler.cs" /> + <Compile Include="MacIdeAppleEvents.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MacPlatform.addin.xml" /> @@ -157,6 +158,9 @@ <ItemGroup> <LibXammac Include="..\..\..\external\libxammac.dylib" /> </ItemGroup> + <ItemGroup> + <None Include="query-vsmac.mm" /> + </ItemGroup> <Target Name="AfterBuild"> <Copy SourceFiles="@(LibXammac)" DestinationFiles="$(OutputPath)\libxammac.dylib" SkipUnchangedFiles="true" /> </Target> diff --git a/main/src/addins/MacPlatform/query-vsmac.mm b/main/src/addins/MacPlatform/query-vsmac.mm new file mode 100644 index 0000000000..88a8a7dac6 --- /dev/null +++ b/main/src/addins/MacPlatform/query-vsmac.mm @@ -0,0 +1,204 @@ +// +// Copyright (c) Microsoft Corp. (https://www.microsoft.com) +// +// Authors: +// Aaron Bockover <abock@microsoft.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// % clang++ -o query-vsmac -ObjC -fcxx-modules -fmodules -Werror -Wall -Wpedantic query-vsmac.mm + +@import AppKit; +@import CoreServices; + +#ifdef __cplusplus +#define VS_EXTERN extern "C" +#else +#define VS_EXTERN extern +#endif + +VS_EXTERN NSRunningApplication ** +VSQueryRunningInstances() +{ + NSArray<NSRunningApplication *> *allApps = NSWorkspace.sharedWorkspace.runningApplications; + if (!allApps) + return nil; + + NSRunningApplication **instances = (NSRunningApplication **)calloc( + [allApps count] + 1, + sizeof(NSRunningApplication *)); + if (!instances) + goto ret; + + int i = 0; + + for (NSRunningApplication *runningApp in NSWorkspace.sharedWorkspace.runningApplications) { + if ([runningApp.bundleIdentifier isEqual: @"com.microsoft.visual-studio"] || + [runningApp.bundleIdentifier isEqual: @"com.xamarin.monodevelop"]) + instances[i++] = [runningApp retain]; + } + +ret: + [allApps release]; + return instances; +} + +VS_EXTERN void +VSFreeRunningInstances(NSRunningApplication **instances) +{ + if (!instances) + return; + + for (int i = 0; instances && instances[i]; i++) + [instances[i] release]; + + free(instances); +} + +static char * +NSStringToUTF8Dup(NSString *str) +{ + if (str) { + const char *utf8str = [str UTF8String]; + if (utf8str) + return strdup(utf8str); + } + + return NULL; +} + +VS_EXTERN char * +VSQueryInstanceCurrentSelectedSolutionPath(NSRunningApplication *runningApplication) +{ + NSAppleEventDescriptor *targetDescriptor = [NSAppleEventDescriptor + descriptorWithProcessIdentifier: runningApplication.processIdentifier]; + + NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor + appleEventWithEventClass: 1448302419 + eventID: 1129534288 + targetDescriptor: targetDescriptor + returnID: kAutoGenerateReturnID + transactionID: kAnyTransactionID]; + + AEDesc aeReply = { 0, }; + + OSErr sendResult = AESendMessage( + [appleEvent aeDesc], + &aeReply, + kAEWaitReply | kAENeverInteract, + kAEDefaultTimeout); + + [targetDescriptor release]; + [appleEvent release]; + + if (sendResult != noErr) { + return NULL; + } + + NSAppleEventDescriptor *reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy: &aeReply]; + NSString *path = [[reply descriptorForKeyword: keyDirectObject] stringValue]; + [reply release]; + + return NSStringToUTF8Dup(path); +} + +VS_EXTERN pid_t +VSGetInstanceProcessIdentifier(NSRunningApplication *instance) +{ + if (!instance) + return -1; + + return instance.processIdentifier; +} + +VS_EXTERN char * +VSGetInstanceBundlePath(NSRunningApplication *instance) +{ + if (!instance || !instance.bundleURL) + return NULL; + + return NSStringToUTF8Dup(instance.bundleURL.path); +} + +VS_EXTERN char * +VSGetInstanceExecutablePath(NSRunningApplication *instance) +{ + if (!instance || !instance.executableURL) + return NULL; + + return NSStringToUTF8Dup(instance.executableURL.path); +} + +VS_EXTERN char * +VSGetInstanceLocalizedName(NSRunningApplication *instance) +{ + if (!instance) + return NULL; + + return NSStringToUTF8Dup(instance.localizedName); +} + +VS_EXTERN char * +VSGetInstanceVersion(NSRunningApplication *instance) +{ + if (!instance || !instance.bundleURL) + return NULL; + + NSBundle *bundle = [NSBundle bundleWithURL: instance.bundleURL]; + if (!bundle) + return NULL; + + id versionValue = [bundle objectForInfoDictionaryKey: @"CFBundleVersion"]; + if (versionValue && [versionValue isKindOfClass: [NSString class]]) + return NSStringToUTF8Dup((NSString *)versionValue); + + return NULL; +} + +int main(int argc, char **argv) +{ + NSRunningApplication **instances = VSQueryRunningInstances(); + + for (int i = 0; instances && instances[i]; i++) { + NSRunningApplication *instance = instances[i]; + + pid_t pid = VSGetInstanceProcessIdentifier(instance); + char *name = VSGetInstanceLocalizedName(instance); + char *version = VSGetInstanceVersion(instance); + char *bunPath = VSGetInstanceBundlePath(instance); + char *exePath = VSGetInstanceExecutablePath(instance); + char *slnPath = VSQueryInstanceCurrentSelectedSolutionPath(instance); + + printf("%s %s\n", name, version); + printf(" pid: %u\n", pid); + printf(" bun: %s\n", bunPath); + printf(" exe: %s\n", exePath); + printf(" sln: %s\n", slnPath); + + free(name); + free(version); + free(bunPath); + free(exePath); + free(slnPath); + } + + VSFreeRunningInstances(instances); + + return 0; +} |