EmbeddedWindow.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Input;
  4. using Avalonia.Platform;
  5. using SPB.Graphics;
  6. using SPB.Platform;
  7. using SPB.Platform.GLX;
  8. using System;
  9. using System.Runtime.InteropServices;
  10. using System.Runtime.Versioning;
  11. using System.Threading.Tasks;
  12. using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
  13. namespace Ryujinx.Ava.UI.Helpers
  14. {
  15. public class EmbeddedWindow : NativeControlHost
  16. {
  17. private WindowProc _wndProcDelegate;
  18. private string _className;
  19. protected GLXWindow X11Window { get; set; }
  20. protected IntPtr WindowHandle { get; set; }
  21. protected IntPtr X11Display { get; set; }
  22. protected IntPtr NsView { get; set; }
  23. protected IntPtr MetalLayer { get; set; }
  24. private UpdateBoundsCallbackDelegate _updateBoundsCallback;
  25. public event EventHandler<IntPtr> WindowCreated;
  26. public event EventHandler<Size> SizeChanged;
  27. protected virtual void OnWindowDestroyed() { }
  28. protected virtual void OnWindowDestroying()
  29. {
  30. WindowHandle = IntPtr.Zero;
  31. X11Display = IntPtr.Zero;
  32. }
  33. public EmbeddedWindow()
  34. {
  35. var stateObserverable = this.GetObservable(BoundsProperty);
  36. stateObserverable.Subscribe(StateChanged);
  37. this.Initialized += NativeEmbeddedWindow_Initialized;
  38. }
  39. public virtual void OnWindowCreated() { }
  40. private void NativeEmbeddedWindow_Initialized(object sender, EventArgs e)
  41. {
  42. OnWindowCreated();
  43. Task.Run(() =>
  44. {
  45. WindowCreated?.Invoke(this, WindowHandle);
  46. });
  47. }
  48. private void StateChanged(Rect rect)
  49. {
  50. SizeChanged?.Invoke(this, rect.Size);
  51. _updateBoundsCallback?.Invoke(rect);
  52. }
  53. protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
  54. {
  55. if (OperatingSystem.IsLinux())
  56. {
  57. return CreateLinux(parent);
  58. }
  59. else if (OperatingSystem.IsWindows())
  60. {
  61. return CreateWin32(parent);
  62. }
  63. else if (OperatingSystem.IsMacOS())
  64. {
  65. return CreateMacOs(parent);
  66. }
  67. return base.CreateNativeControlCore(parent);
  68. }
  69. protected override void DestroyNativeControlCore(IPlatformHandle control)
  70. {
  71. OnWindowDestroying();
  72. if (OperatingSystem.IsLinux())
  73. {
  74. DestroyLinux();
  75. }
  76. else if (OperatingSystem.IsWindows())
  77. {
  78. DestroyWin32(control);
  79. }
  80. else if (OperatingSystem.IsMacOS())
  81. {
  82. DestroyMacOS();
  83. }
  84. else
  85. {
  86. base.DestroyNativeControlCore(control);
  87. }
  88. OnWindowDestroyed();
  89. }
  90. [SupportedOSPlatform("linux")]
  91. protected virtual IPlatformHandle CreateLinux(IPlatformHandle parent)
  92. {
  93. X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
  94. WindowHandle = X11Window.WindowHandle.RawHandle;
  95. X11Display = X11Window.DisplayHandle.RawHandle;
  96. return new PlatformHandle(WindowHandle, "X11");
  97. }
  98. [SupportedOSPlatform("windows")]
  99. IPlatformHandle CreateWin32(IPlatformHandle parent)
  100. {
  101. _className = "NativeWindow-" + Guid.NewGuid();
  102. _wndProcDelegate = WndProc;
  103. var wndClassEx = new WNDCLASSEX
  104. {
  105. cbSize = Marshal.SizeOf<WNDCLASSEX>(),
  106. hInstance = GetModuleHandle(null),
  107. lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
  108. style = ClassStyles.CS_OWNDC,
  109. lpszClassName = Marshal.StringToHGlobalUni(_className),
  110. hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW)
  111. };
  112. var atom = RegisterClassEx(ref wndClassEx);
  113. var handle = CreateWindowEx(
  114. 0,
  115. _className,
  116. "NativeWindow",
  117. WindowStyles.WS_CHILD,
  118. 0,
  119. 0,
  120. 640,
  121. 480,
  122. parent.Handle,
  123. IntPtr.Zero,
  124. IntPtr.Zero,
  125. IntPtr.Zero);
  126. WindowHandle = handle;
  127. Marshal.FreeHGlobal(wndClassEx.lpszClassName);
  128. return new PlatformHandle(WindowHandle, "HWND");
  129. }
  130. [SupportedOSPlatform("windows")]
  131. IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
  132. {
  133. var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF);
  134. var root = VisualRoot as Window;
  135. bool isLeft = false;
  136. switch (msg)
  137. {
  138. case WindowsMessages.LBUTTONDOWN:
  139. case WindowsMessages.RBUTTONDOWN:
  140. isLeft = msg == WindowsMessages.LBUTTONDOWN;
  141. this.RaiseEvent(new PointerPressedEventArgs(
  142. this,
  143. new Pointer(0, PointerType.Mouse, true),
  144. root,
  145. this.TranslatePoint(point, root).Value,
  146. (ulong)Environment.TickCount64,
  147. new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed),
  148. KeyModifiers.None));
  149. break;
  150. case WindowsMessages.LBUTTONUP:
  151. case WindowsMessages.RBUTTONUP:
  152. isLeft = msg == WindowsMessages.LBUTTONUP;
  153. this.RaiseEvent(new PointerReleasedEventArgs(
  154. this,
  155. new Pointer(0, PointerType.Mouse, true),
  156. root,
  157. this.TranslatePoint(point, root).Value,
  158. (ulong)Environment.TickCount64,
  159. new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased),
  160. KeyModifiers.None,
  161. isLeft ? MouseButton.Left : MouseButton.Right));
  162. break;
  163. case WindowsMessages.MOUSEMOVE:
  164. this.RaiseEvent(new PointerEventArgs(
  165. PointerMovedEvent,
  166. this,
  167. new Pointer(0, PointerType.Mouse, true),
  168. root,
  169. this.TranslatePoint(point, root).Value,
  170. (ulong)Environment.TickCount64,
  171. new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
  172. KeyModifiers.None));
  173. break;
  174. }
  175. return DefWindowProc(hWnd, msg, wParam, lParam);
  176. }
  177. [SupportedOSPlatform("macos")]
  178. IPlatformHandle CreateMacOs(IPlatformHandle parent)
  179. {
  180. MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
  181. NsView = nsView;
  182. return new PlatformHandle(nsView, "NSView");
  183. }
  184. void DestroyLinux()
  185. {
  186. X11Window?.Dispose();
  187. }
  188. [SupportedOSPlatform("windows")]
  189. void DestroyWin32(IPlatformHandle handle)
  190. {
  191. DestroyWindow(handle.Handle);
  192. UnregisterClass(_className, GetModuleHandle(null));
  193. }
  194. [SupportedOSPlatform("macos")]
  195. void DestroyMacOS()
  196. {
  197. MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
  198. }
  199. }
  200. }