/ src / Ryujinx.Gtk3 / UI / Helper / MetalHelper.cs
MetalHelper.cs
  1  using Gdk;
  2  using System;
  3  using System.Runtime.InteropServices;
  4  using System.Runtime.Versioning;
  5  
  6  namespace Ryujinx.UI.Helper
  7  {
  8      public delegate void UpdateBoundsCallbackDelegate(Window window);
  9  
 10      [SupportedOSPlatform("macos")]
 11      static partial class MetalHelper
 12      {
 13          private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
 14  
 15          private readonly struct Selector
 16          {
 17              public readonly IntPtr NativePtr;
 18  
 19              public unsafe Selector(string value)
 20              {
 21                  int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
 22                  byte* data = stackalloc byte[size];
 23  
 24                  fixed (char* pValue = value)
 25                  {
 26                      System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
 27                  }
 28  
 29                  NativePtr = sel_registerName(data);
 30              }
 31  
 32              public static implicit operator Selector(string value) => new(value);
 33          }
 34  
 35          private static unsafe IntPtr GetClass(string value)
 36          {
 37              int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
 38              byte* data = stackalloc byte[size];
 39  
 40              fixed (char* pValue = value)
 41              {
 42                  System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
 43              }
 44  
 45              return objc_getClass(data);
 46          }
 47  
 48          private struct NsPoint
 49          {
 50              public double X;
 51              public double Y;
 52  
 53              public NsPoint(double x, double y)
 54              {
 55                  X = x;
 56                  Y = y;
 57              }
 58          }
 59  
 60          private struct NsRect
 61          {
 62              public NsPoint Pos;
 63              public NsPoint Size;
 64  
 65              public NsRect(double x, double y, double width, double height)
 66              {
 67                  Pos = new NsPoint(x, y);
 68                  Size = new NsPoint(width, height);
 69              }
 70          }
 71  
 72          public static IntPtr GetMetalLayer(Display display, Window window, out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
 73          {
 74              nsView = gdk_quartz_window_get_nsview(window.Handle);
 75  
 76              // Create a new CAMetalLayer.
 77              IntPtr layerClass = GetClass("CAMetalLayer");
 78              IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
 79              objc_msgSend(metalLayer, "init");
 80  
 81              // Create a child NSView to render into.
 82              IntPtr nsViewClass = GetClass("NSView");
 83              IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
 84              objc_msgSend(child, "init", new NsRect());
 85  
 86              // Add it as a child.
 87              objc_msgSend(nsView, "addSubview:", child);
 88  
 89              // Make its renderer our metal layer.
 90              objc_msgSend(child, "setWantsLayer:", (byte)1);
 91              objc_msgSend(child, "setLayer:", metalLayer);
 92              objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor);
 93  
 94              // Set the frame position/location.
 95              updateBounds = (Window window) =>
 96              {
 97                  window.GetPosition(out int x, out int y);
 98                  int width = window.Width;
 99                  int height = window.Height;
100                  objc_msgSend(child, "setFrame:", new NsRect(x, y, width, height));
101              };
102  
103              updateBounds(window);
104  
105              return metalLayer;
106          }
107  
108          [LibraryImport(LibObjCImport)]
109          private static unsafe partial IntPtr sel_registerName(byte* data);
110  
111          [LibraryImport(LibObjCImport)]
112          private static unsafe partial IntPtr objc_getClass(byte* data);
113  
114          [LibraryImport(LibObjCImport)]
115          private static partial void objc_msgSend(IntPtr receiver, Selector selector);
116  
117          [LibraryImport(LibObjCImport)]
118          private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
119  
120          [LibraryImport(LibObjCImport)]
121          private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
122  
123          [LibraryImport(LibObjCImport)]
124          private static partial void objc_msgSend(IntPtr receiver, Selector selector, NsRect point);
125  
126          [LibraryImport(LibObjCImport)]
127          private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
128  
129          [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
130          private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
131  
132          [LibraryImport("libgdk-3.0.dylib")]
133          private static partial IntPtr gdk_quartz_window_get_nsview(IntPtr gdkWindow);
134      }
135  }