/ src / Ryujinx / UI / Renderer / EmbeddedWindow.cs
EmbeddedWindow.cs
  1  using Avalonia;
  2  using Avalonia.Controls;
  3  using Avalonia.Input;
  4  using Avalonia.Platform;
  5  using Ryujinx.Common.Configuration;
  6  using Ryujinx.UI.Common.Configuration;
  7  using Ryujinx.UI.Common.Helper;
  8  using SPB.Graphics;
  9  using SPB.Platform;
 10  using SPB.Platform.GLX;
 11  using SPB.Platform.X11;
 12  using SPB.Windowing;
 13  using System;
 14  using System.Runtime.InteropServices;
 15  using System.Runtime.Versioning;
 16  using System.Threading.Tasks;
 17  using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
 18  
 19  namespace Ryujinx.Ava.UI.Renderer
 20  {
 21      public class EmbeddedWindow : NativeControlHost
 22      {
 23          private WindowProc _wndProcDelegate;
 24          private string _className;
 25  
 26          protected GLXWindow X11Window { get; set; }
 27  
 28          protected IntPtr WindowHandle { get; set; }
 29          protected IntPtr X11Display { get; set; }
 30          protected IntPtr NsView { get; set; }
 31          protected IntPtr MetalLayer { get; set; }
 32  
 33          public delegate void UpdateBoundsCallbackDelegate(Rect rect);
 34          private UpdateBoundsCallbackDelegate _updateBoundsCallback;
 35  
 36          public event EventHandler<IntPtr> WindowCreated;
 37          public event EventHandler<Size> BoundsChanged;
 38  
 39          public EmbeddedWindow()
 40          {
 41              this.GetObservable(BoundsProperty).Subscribe(StateChanged);
 42  
 43              Initialized += OnNativeEmbeddedWindowCreated;
 44          }
 45  
 46          public virtual void OnWindowCreated() { }
 47  
 48          protected virtual void OnWindowDestroyed() { }
 49  
 50          protected virtual void OnWindowDestroying()
 51          {
 52              WindowHandle = IntPtr.Zero;
 53              X11Display = IntPtr.Zero;
 54              NsView = IntPtr.Zero;
 55              MetalLayer = IntPtr.Zero;
 56          }
 57  
 58          private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e)
 59          {
 60              OnWindowCreated();
 61  
 62              Task.Run(() =>
 63              {
 64                  WindowCreated?.Invoke(this, WindowHandle);
 65              });
 66          }
 67  
 68          private void StateChanged(Rect rect)
 69          {
 70              BoundsChanged?.Invoke(this, rect.Size);
 71              _updateBoundsCallback?.Invoke(rect);
 72          }
 73  
 74          protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle control)
 75          {
 76              if (OperatingSystem.IsLinux())
 77              {
 78                  return CreateLinux(control);
 79              }
 80  
 81              if (OperatingSystem.IsWindows())
 82              {
 83                  return CreateWin32(control);
 84              }
 85  
 86              if (OperatingSystem.IsMacOS())
 87              {
 88                  return CreateMacOS();
 89              }
 90  
 91              return base.CreateNativeControlCore(control);
 92          }
 93  
 94          protected override void DestroyNativeControlCore(IPlatformHandle control)
 95          {
 96              OnWindowDestroying();
 97  
 98              if (OperatingSystem.IsLinux())
 99              {
100                  DestroyLinux();
101              }
102              else if (OperatingSystem.IsWindows())
103              {
104                  DestroyWin32(control);
105              }
106              else if (OperatingSystem.IsMacOS())
107              {
108                  DestroyMacOS();
109              }
110              else
111              {
112                  base.DestroyNativeControlCore(control);
113              }
114  
115              OnWindowDestroyed();
116          }
117  
118          [SupportedOSPlatform("linux")]
119          private IPlatformHandle CreateLinux(IPlatformHandle control)
120          {
121              if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
122              {
123                  X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
124                  X11Window.Hide();
125              }
126              else
127              {
128                  X11Window = PlatformHelper.CreateOpenGLWindow(new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false), 0, 0, 100, 100) as GLXWindow;
129              }
130  
131              WindowHandle = X11Window.WindowHandle.RawHandle;
132              X11Display = X11Window.DisplayHandle.RawHandle;
133  
134              return new PlatformHandle(WindowHandle, "X11");
135          }
136  
137          [SupportedOSPlatform("windows")]
138          IPlatformHandle CreateWin32(IPlatformHandle control)
139          {
140              _className = "NativeWindow-" + Guid.NewGuid();
141  
142              _wndProcDelegate = delegate (IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
143              {
144                  switch (msg)
145                  {
146                      case WindowsMessages.NcHitTest:
147                          return -1;
148                  }
149  
150                  return DefWindowProc(hWnd, msg, wParam, lParam);
151              };
152  
153              WndClassEx wndClassEx = new()
154              {
155                  cbSize = Marshal.SizeOf<WndClassEx>(),
156                  hInstance = GetModuleHandle(null),
157                  lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
158                  style = ClassStyles.CsOwndc,
159                  lpszClassName = Marshal.StringToHGlobalUni(_className),
160                  hCursor = CreateArrowCursor()
161              };
162  
163              RegisterClassEx(ref wndClassEx);
164  
165              WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WsChild, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
166  
167              SetWindowLongPtrW(control.Handle, GWLP_WNDPROC, wndClassEx.lpfnWndProc);
168  
169              Marshal.FreeHGlobal(wndClassEx.lpszClassName);
170  
171              return new PlatformHandle(WindowHandle, "HWND");
172          }
173  
174          [SupportedOSPlatform("macos")]
175          IPlatformHandle CreateMacOS()
176          {
177              // Create a new CAMetalLayer.
178              ObjectiveC.Object layerObject = new("CAMetalLayer");
179              ObjectiveC.Object metalLayer = layerObject.GetFromMessage("alloc");
180              metalLayer.SendMessage("init");
181  
182              // Create a child NSView to render into.
183              ObjectiveC.Object nsViewObject = new("NSView");
184              ObjectiveC.Object child = nsViewObject.GetFromMessage("alloc");
185              child.SendMessage("init", new ObjectiveC.NSRect(0, 0, 0, 0));
186  
187              // Make its renderer our metal layer.
188              child.SendMessage("setWantsLayer:", 1);
189              child.SendMessage("setLayer:", metalLayer);
190              metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor);
191  
192              // Ensure the scale factor is up to date.
193              _updateBoundsCallback = rect =>
194              {
195                  metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor);
196              };
197  
198              IntPtr nsView = child.ObjPtr;
199              MetalLayer = metalLayer.ObjPtr;
200              NsView = nsView;
201  
202              return new PlatformHandle(nsView, "NSView");
203          }
204  
205          [SupportedOSPlatform("Linux")]
206          void DestroyLinux()
207          {
208              X11Window?.Dispose();
209          }
210  
211          [SupportedOSPlatform("windows")]
212          void DestroyWin32(IPlatformHandle handle)
213          {
214              DestroyWindow(handle.Handle);
215              UnregisterClass(_className, GetModuleHandle(null));
216          }
217  
218          [SupportedOSPlatform("macos")]
219  #pragma warning disable CA1822 // Mark member as static
220          void DestroyMacOS()
221          {
222              // TODO
223          }
224  #pragma warning restore CA1822
225      }
226  }