EmbeddedWindow.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. namespace Ryujinx.Ava.UI.Renderer
  19. {
  20. public class EmbeddedWindow : NativeControlHost
  21. {
  22. private WindowProc _wndProcDelegate;
  23. private string _className;
  24. protected GLXWindow X11Window { get; set; }
  25. protected IntPtr WindowHandle { get; set; }
  26. protected IntPtr X11Display { get; set; }
  27. protected IntPtr NsView { get; set; }
  28. protected IntPtr MetalLayer { get; set; }
  29. public delegate void UpdateBoundsCallbackDelegate(Rect rect);
  30. private UpdateBoundsCallbackDelegate _updateBoundsCallback;
  31. public event EventHandler<IntPtr> WindowCreated;
  32. public event EventHandler<Size> SizeChanged;
  33. public EmbeddedWindow()
  34. {
  35. this.GetObservable(BoundsProperty).Subscribe(StateChanged);
  36. Initialized += OnNativeEmbeddedWindowCreated;
  37. }
  38. public virtual void OnWindowCreated() { }
  39. protected virtual void OnWindowDestroyed() { }
  40. protected virtual void OnWindowDestroying()
  41. {
  42. WindowHandle = IntPtr.Zero;
  43. X11Display = IntPtr.Zero;
  44. NsView = IntPtr.Zero;
  45. MetalLayer = IntPtr.Zero;
  46. }
  47. private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e)
  48. {
  49. OnWindowCreated();
  50. Task.Run(() =>
  51. {
  52. WindowCreated?.Invoke(this, WindowHandle);
  53. });
  54. }
  55. private void StateChanged(Rect rect)
  56. {
  57. SizeChanged?.Invoke(this, rect.Size);
  58. _updateBoundsCallback?.Invoke(rect);
  59. }
  60. protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle control)
  61. {
  62. if (OperatingSystem.IsLinux())
  63. {
  64. return CreateLinux(control);
  65. }
  66. else if (OperatingSystem.IsWindows())
  67. {
  68. return CreateWin32(control);
  69. }
  70. else if (OperatingSystem.IsMacOS())
  71. {
  72. return CreateMacOS();
  73. }
  74. return base.CreateNativeControlCore(control);
  75. }
  76. protected override void DestroyNativeControlCore(IPlatformHandle control)
  77. {
  78. OnWindowDestroying();
  79. if (OperatingSystem.IsLinux())
  80. {
  81. DestroyLinux();
  82. }
  83. else if (OperatingSystem.IsWindows())
  84. {
  85. DestroyWin32(control);
  86. }
  87. else if (OperatingSystem.IsMacOS())
  88. {
  89. DestroyMacOS();
  90. }
  91. else
  92. {
  93. base.DestroyNativeControlCore(control);
  94. }
  95. OnWindowDestroyed();
  96. }
  97. [SupportedOSPlatform("linux")]
  98. private IPlatformHandle CreateLinux(IPlatformHandle control)
  99. {
  100. if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
  101. {
  102. X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
  103. X11Window.Hide();
  104. }
  105. else
  106. {
  107. X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
  108. }
  109. WindowHandle = X11Window.WindowHandle.RawHandle;
  110. X11Display = X11Window.DisplayHandle.RawHandle;
  111. return new PlatformHandle(WindowHandle, "X11");
  112. }
  113. [SupportedOSPlatform("windows")]
  114. IPlatformHandle CreateWin32(IPlatformHandle control)
  115. {
  116. _className = "NativeWindow-" + Guid.NewGuid();
  117. _wndProcDelegate = delegate (IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
  118. {
  119. if (VisualRoot != null)
  120. {
  121. if (msg == WindowsMessages.LBUTTONDOWN ||
  122. msg == WindowsMessages.RBUTTONDOWN ||
  123. msg == WindowsMessages.LBUTTONUP ||
  124. msg == WindowsMessages.RBUTTONUP ||
  125. msg == WindowsMessages.MOUSEMOVE)
  126. {
  127. Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
  128. Pointer pointer = new(0, PointerType.Mouse, true);
  129. switch (msg)
  130. {
  131. case WindowsMessages.LBUTTONDOWN:
  132. case WindowsMessages.RBUTTONDOWN:
  133. {
  134. bool isLeft = msg == WindowsMessages.LBUTTONDOWN;
  135. RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
  136. PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
  137. var evnt = new PointerPressedEventArgs(
  138. this,
  139. pointer,
  140. VisualRoot,
  141. rootVisualPosition,
  142. (ulong)Environment.TickCount64,
  143. properties,
  144. KeyModifiers.None);
  145. RaiseEvent(evnt);
  146. break;
  147. }
  148. case WindowsMessages.LBUTTONUP:
  149. case WindowsMessages.RBUTTONUP:
  150. {
  151. bool isLeft = msg == WindowsMessages.LBUTTONUP;
  152. RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
  153. PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
  154. var evnt = new PointerReleasedEventArgs(
  155. this,
  156. pointer,
  157. VisualRoot,
  158. rootVisualPosition,
  159. (ulong)Environment.TickCount64,
  160. properties,
  161. KeyModifiers.None,
  162. isLeft ? MouseButton.Left : MouseButton.Right);
  163. RaiseEvent(evnt);
  164. break;
  165. }
  166. case WindowsMessages.MOUSEMOVE:
  167. {
  168. var evnt = new PointerEventArgs(
  169. PointerMovedEvent,
  170. this,
  171. pointer,
  172. VisualRoot,
  173. rootVisualPosition,
  174. (ulong)Environment.TickCount64,
  175. new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
  176. KeyModifiers.None);
  177. RaiseEvent(evnt);
  178. break;
  179. }
  180. }
  181. }
  182. }
  183. return DefWindowProc(hWnd, msg, wParam, lParam);
  184. };
  185. WNDCLASSEX wndClassEx = new()
  186. {
  187. cbSize = Marshal.SizeOf<WNDCLASSEX>(),
  188. hInstance = GetModuleHandle(null),
  189. lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
  190. style = ClassStyles.CS_OWNDC,
  191. lpszClassName = Marshal.StringToHGlobalUni(_className),
  192. hCursor = CreateArrowCursor()
  193. };
  194. RegisterClassEx(ref wndClassEx);
  195. WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  196. Marshal.FreeHGlobal(wndClassEx.lpszClassName);
  197. return new PlatformHandle(WindowHandle, "HWND");
  198. }
  199. [SupportedOSPlatform("macos")]
  200. IPlatformHandle CreateMacOS()
  201. {
  202. // Create a new CAMetalLayer.
  203. IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer");
  204. IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc");
  205. ObjectiveC.objc_msgSend(metalLayer, "init");
  206. // Create a child NSView to render into.
  207. IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView");
  208. IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc");
  209. ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0));
  210. // Make its renderer our metal layer.
  211. ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1);
  212. ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer);
  213. ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
  214. // Ensure the scale factor is up to date.
  215. _updateBoundsCallback = rect =>
  216. {
  217. ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
  218. };
  219. IntPtr nsView = child;
  220. MetalLayer = metalLayer;
  221. NsView = nsView;
  222. return new PlatformHandle(nsView, "NSView");
  223. }
  224. [SupportedOSPlatform("Linux")]
  225. void DestroyLinux()
  226. {
  227. X11Window?.Dispose();
  228. }
  229. [SupportedOSPlatform("windows")]
  230. void DestroyWin32(IPlatformHandle handle)
  231. {
  232. DestroyWindow(handle.Handle);
  233. UnregisterClass(_className, GetModuleHandle(null));
  234. }
  235. [SupportedOSPlatform("macos")]
  236. void DestroyMacOS()
  237. {
  238. // TODO
  239. }
  240. }
  241. }