Эх сурвалжийг харах

`ObjectiveC` Helper Class (#4286)

* `NativeMacOS` Helper Class

* Corrections

* Make CFString IDisposable

* Fix `openURL:`

* `dealloc` metal layer

* Remove releases

* Use NSString

* Update Ryujinx.Ui.Common/Helper/NativeMacOS.cs

Co-authored-by: merry <git@mary.rs>

* Programatically select updates in Finder

* Address feedback

* Feedback

* Ptr

* Fix whoopsie

* Ack suggestions

* Update Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* GDK Suggestions

---------

Co-authored-by: merry <git@mary.rs>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Isaac Marovitz 3 жил өмнө
parent
commit
7bae440d3a

+ 0 - 127
Ryujinx.Ava/UI/Helpers/MetalHelper.cs

@@ -1,127 +0,0 @@
-using System;
-using System.Runtime.Versioning;
-using System.Runtime.InteropServices;
-using Avalonia;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    public delegate void UpdateBoundsCallbackDelegate(Rect rect);
-
-    [SupportedOSPlatform("macos")]
-    static partial class MetalHelper
-    {
-        private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
-
-        private struct Selector
-        {
-            public readonly IntPtr NativePtr;
-
-            public unsafe Selector(string value)
-            {
-                int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
-                byte* data = stackalloc byte[size];
-
-                fixed (char* pValue = value)
-                {
-                    System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
-                }
-
-                NativePtr = sel_registerName(data);
-            }
-
-            public static implicit operator Selector(string value) => new Selector(value);
-        }
-
-        private static unsafe IntPtr GetClass(string value)
-        {
-            int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
-            byte* data = stackalloc byte[size];
-
-            fixed (char* pValue = value)
-            {
-                System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
-            }
-
-            return objc_getClass(data);
-        }
-
-        private struct NSPoint
-        {
-            public double X;
-            public double Y;
-
-            public NSPoint(double x, double y)
-            {
-                X = x;
-                Y = y;
-            }
-        }
-
-        private struct NSRect
-        {
-            public NSPoint Pos;
-            public NSPoint Size;
-
-            public NSRect(double x, double y, double width, double height)
-            {
-                Pos = new NSPoint(x, y);
-                Size = new NSPoint(width, height);
-            }
-        }
-
-        public static IntPtr GetMetalLayer(out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
-        {
-            // Create a new CAMetalLayer.
-            IntPtr layerClass = GetClass("CAMetalLayer");
-            IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
-            objc_msgSend(metalLayer, "init");
-
-            // Create a child NSView to render into.
-            IntPtr nsViewClass = GetClass("NSView");
-            IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
-            objc_msgSend(child, "init", new NSRect(0, 0, 0, 0));
-
-            // Make its renderer our metal layer.
-            objc_msgSend(child, "setWantsLayer:", (byte)1);
-            objc_msgSend(child, "setLayer:", metalLayer);
-            objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
-
-            // Ensure the scale factor is up to date.
-            updateBounds = (Rect rect) => {
-                objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
-            };
-
-            nsView = child;
-            return metalLayer;
-        }
-
-        public static void DestroyMetalLayer(IntPtr nsView, IntPtr metalLayer)
-        {
-            // TODO
-        }
-
-        [LibraryImport(LibObjCImport)]
-        private static unsafe partial IntPtr sel_registerName(byte* data);
-
-        [LibraryImport(LibObjCImport)]
-        private static unsafe partial IntPtr objc_getClass(byte* data);
-
-        [LibraryImport(LibObjCImport)]
-        private static partial void objc_msgSend(IntPtr receiver, Selector selector);
-
-        [LibraryImport(LibObjCImport)]
-        private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
-
-        [LibraryImport(LibObjCImport)]
-        private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
-
-        [LibraryImport(LibObjCImport)]
-        private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
-
-        [LibraryImport(LibObjCImport)]
-        private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
-
-        [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
-        private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
-    }
-}

+ 25 - 3
Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs

@@ -2,9 +2,9 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Platform;
-using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
 using SPB.Graphics;
 using SPB.Platform;
 using SPB.Platform.GLX;
@@ -30,6 +30,7 @@ namespace Ryujinx.Ava.UI.Renderer
         protected IntPtr NsView       { get; set; }
         protected IntPtr MetalLayer   { get; set; }
 
+        public delegate void UpdateBoundsCallbackDelegate(Rect rect);
         private UpdateBoundsCallbackDelegate _updateBoundsCallback;
 
         public event EventHandler<IntPtr> WindowCreated;
@@ -237,8 +238,29 @@ namespace Ryujinx.Ava.UI.Renderer
         [SupportedOSPlatform("macos")]
         IPlatformHandle CreateMacOS()
         {
-            MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
+            // Create a new CAMetalLayer.
+            IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer");
+            IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc");
+            ObjectiveC.objc_msgSend(metalLayer, "init");
+
+            // Create a child NSView to render into.
+            IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView");
+            IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc");
+            ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0));
+
+            // Make its renderer our metal layer.
+            ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1);
+            ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer);
+            ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
+
+            // Ensure the scale factor is up to date.
+            _updateBoundsCallback = rect =>
+            {
+                ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
+            };
 
+            IntPtr nsView = child;
+            MetalLayer = metalLayer;
             NsView = nsView;
 
             return new PlatformHandle(nsView, "NSView");
@@ -260,7 +282,7 @@ namespace Ryujinx.Ava.UI.Renderer
         [SupportedOSPlatform("macos")]
         void DestroyMacOS()
         {
-            MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
+            // TODO
         }
     }
 }

+ 97 - 0
Ryujinx.Ui.Common/Helper/ObjectiveC.cs

@@ -0,0 +1,97 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Text;
+
+namespace Ryujinx.Ui.Common.Helper
+{
+    [SupportedOSPlatform("macos")]
+    public static partial class ObjectiveC
+    {
+        private const string ObjCRuntime = "/usr/lib/libobjc.A.dylib";
+
+        [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)]
+        private static unsafe partial IntPtr sel_getUid(string name);
+
+        [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)]
+        public static partial IntPtr objc_getClass(string name);
+
+        [LibraryImport(ObjCRuntime)]
+        public static partial void objc_msgSend(IntPtr receiver, Selector selector);
+
+        [LibraryImport(ObjCRuntime)]
+        public static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
+
+        [LibraryImport(ObjCRuntime)]
+        public static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
+
+        [LibraryImport(ObjCRuntime)]
+        public static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
+
+        [LibraryImport(ObjCRuntime)]
+        public static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
+
+        [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")]
+        public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
+
+        [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")]
+        public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param);
+
+        [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend", StringMarshalling = StringMarshalling.Utf8)]
+        public static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, string param);
+
+        [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static partial bool bool_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param);
+
+        public struct Selector
+        {
+            public readonly IntPtr SelPtr;
+
+            public unsafe Selector(string name)
+            {
+                SelPtr = sel_getUid(name);
+            }
+
+            public static implicit operator Selector(string value) => new(value);
+        }
+
+        public struct NSString
+        {
+            public readonly IntPtr StrPtr;
+
+            public NSString(string aString)
+            {
+                IntPtr nsString = objc_getClass("NSString");
+                StrPtr = IntPtr_objc_msgSend(nsString, "stringWithUTF8String:", aString);
+            }
+
+            public static implicit operator IntPtr(NSString nsString) => nsString.StrPtr;
+        }
+
+        public readonly struct NSPoint
+        {
+            public readonly double X;
+            public readonly double Y;
+
+            public NSPoint(double x, double y)
+            {
+                X = x;
+                Y = y;
+            }
+        }
+
+        public readonly struct NSRect
+        {
+            public readonly NSPoint Pos;
+            public readonly NSPoint Size;
+
+            public NSRect(double x, double y, double width, double height)
+            {
+                Pos = new NSPoint(x, y);
+                Size = new NSPoint(width, height);
+            }
+        }
+    }
+}

+ 19 - 2
Ryujinx.Ui.Common/Helper/OpenHelper.cs

@@ -55,7 +55,17 @@ namespace Ryujinx.Ui.Common.Helper
                 }
                 else if (OperatingSystem.IsMacOS())
                 {
-                    Process.Start("open", $"-R \"{path}\"");
+                    ObjectiveC.NSString nsStringPath = new(path);
+                    IntPtr nsUrl = ObjectiveC.objc_getClass("NSURL");
+                    var urlPtr = ObjectiveC.IntPtr_objc_msgSend(nsUrl, "fileURLWithPath:", nsStringPath);
+
+                    IntPtr nsArray = ObjectiveC.objc_getClass("NSArray");
+                    IntPtr urlArray = ObjectiveC.IntPtr_objc_msgSend(nsArray, "arrayWithObject:", urlPtr);
+
+                    IntPtr nsWorkspace = ObjectiveC.objc_getClass("NSWorkspace");
+                    IntPtr sharedWorkspace = ObjectiveC.IntPtr_objc_msgSend(nsWorkspace, "sharedWorkspace");
+
+                    ObjectiveC.objc_msgSend(sharedWorkspace, "activateFileViewerSelectingURLs:", urlArray);
                 }
                 else if (OperatingSystem.IsLinux())
                 {
@@ -84,7 +94,14 @@ namespace Ryujinx.Ui.Common.Helper
             }
             else if (OperatingSystem.IsMacOS())
             {
-                Process.Start("open", url);
+                ObjectiveC.NSString nsStringPath = new(url);
+                IntPtr nsUrl = ObjectiveC.objc_getClass("NSURL");
+                var urlPtr = ObjectiveC.IntPtr_objc_msgSend(nsUrl, "URLWithString:", nsStringPath);
+
+                IntPtr nsWorkspace = ObjectiveC.objc_getClass("NSWorkspace");
+                IntPtr sharedWorkspace = ObjectiveC.IntPtr_objc_msgSend(nsWorkspace, "sharedWorkspace");
+
+                ObjectiveC.bool_objc_msgSend(sharedWorkspace, "openURL:", urlPtr);
             }
             else
             {