diff options
Diffstat (limited to 'src/Ryujinx/UI/Renderer/EmbeddedWindow.cs')
-rw-r--r-- | src/Ryujinx/UI/Renderer/EmbeddedWindow.cs | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs new file mode 100644 index 00000000..3bf19b43 --- /dev/null +++ b/src/Ryujinx/UI/Renderer/EmbeddedWindow.cs @@ -0,0 +1,294 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Platform; +using Ryujinx.Common.Configuration; +using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.Common.Helper; +using SPB.Graphics; +using SPB.Platform; +using SPB.Platform.GLX; +using SPB.Platform.X11; +using SPB.Windowing; +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; + +namespace Ryujinx.Ava.UI.Renderer +{ + public class EmbeddedWindow : NativeControlHost + { + private WindowProc _wndProcDelegate; + private string _className; + + protected GLXWindow X11Window { get; set; } + + protected IntPtr WindowHandle { get; set; } + protected IntPtr X11Display { get; set; } + protected IntPtr NsView { get; set; } + protected IntPtr MetalLayer { get; set; } + + public delegate void UpdateBoundsCallbackDelegate(Rect rect); + private UpdateBoundsCallbackDelegate _updateBoundsCallback; + + public event EventHandler<IntPtr> WindowCreated; + public event EventHandler<Size> BoundsChanged; + + public EmbeddedWindow() + { + this.GetObservable(BoundsProperty).Subscribe(StateChanged); + + Initialized += OnNativeEmbeddedWindowCreated; + } + + public virtual void OnWindowCreated() { } + + protected virtual void OnWindowDestroyed() { } + + protected virtual void OnWindowDestroying() + { + WindowHandle = IntPtr.Zero; + X11Display = IntPtr.Zero; + NsView = IntPtr.Zero; + MetalLayer = IntPtr.Zero; + } + + private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e) + { + OnWindowCreated(); + + Task.Run(() => + { + WindowCreated?.Invoke(this, WindowHandle); + }); + } + + private void StateChanged(Rect rect) + { + BoundsChanged?.Invoke(this, rect.Size); + _updateBoundsCallback?.Invoke(rect); + } + + protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle control) + { + if (OperatingSystem.IsLinux()) + { + return CreateLinux(control); + } + + if (OperatingSystem.IsWindows()) + { + return CreateWin32(control); + } + + if (OperatingSystem.IsMacOS()) + { + return CreateMacOS(); + } + + return base.CreateNativeControlCore(control); + } + + protected override void DestroyNativeControlCore(IPlatformHandle control) + { + OnWindowDestroying(); + + if (OperatingSystem.IsLinux()) + { + DestroyLinux(); + } + else if (OperatingSystem.IsWindows()) + { + DestroyWin32(control); + } + else if (OperatingSystem.IsMacOS()) + { + DestroyMacOS(); + } + else + { + base.DestroyNativeControlCore(control); + } + + OnWindowDestroyed(); + } + + [SupportedOSPlatform("linux")] + private IPlatformHandle CreateLinux(IPlatformHandle control) + { + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) + { + X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle)); + X11Window.Hide(); + } + else + { + X11Window = PlatformHelper.CreateOpenGLWindow(new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false), 0, 0, 100, 100) as GLXWindow; + } + + WindowHandle = X11Window.WindowHandle.RawHandle; + X11Display = X11Window.DisplayHandle.RawHandle; + + return new PlatformHandle(WindowHandle, "X11"); + } + + [SupportedOSPlatform("windows")] + IPlatformHandle CreateWin32(IPlatformHandle control) + { + _className = "NativeWindow-" + Guid.NewGuid(); + + _wndProcDelegate = delegate (IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam) + { + if (VisualRoot != null) + { + if (msg == WindowsMessages.Lbuttondown || + msg == WindowsMessages.Rbuttondown || + msg == WindowsMessages.Lbuttonup || + msg == WindowsMessages.Rbuttonup || + msg == WindowsMessages.Mousemove) + { + Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), this).Value; + Pointer pointer = new(0, PointerType.Mouse, true); + +#pragma warning disable CS0618 // Type or member is obsolete (As of Avalonia 11, the constructors for PointerPressedEventArgs & PointerEventArgs are marked as obsolete) + switch (msg) + { + case WindowsMessages.Lbuttondown: + case WindowsMessages.Rbuttondown: + { + bool isLeft = msg == WindowsMessages.Lbuttondown; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); + + var evnt = new PointerPressedEventArgs( + this, + pointer, + this, + rootVisualPosition, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None); + + RaiseEvent(evnt); + + break; + } + case WindowsMessages.Lbuttonup: + case WindowsMessages.Rbuttonup: + { + bool isLeft = msg == WindowsMessages.Lbuttonup; + RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; + PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); + + var evnt = new PointerReleasedEventArgs( + this, + pointer, + this, + rootVisualPosition, + (ulong)Environment.TickCount64, + properties, + KeyModifiers.None, + isLeft ? MouseButton.Left : MouseButton.Right); + + RaiseEvent(evnt); + + break; + } + case WindowsMessages.Mousemove: + { + var evnt = new PointerEventArgs( + PointerMovedEvent, + this, + pointer, + this, + rootVisualPosition, + (ulong)Environment.TickCount64, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other), + KeyModifiers.None); + + RaiseEvent(evnt); + + break; + } + } +#pragma warning restore CS0618 + } + } + + return DefWindowProc(hWnd, msg, wParam, lParam); + }; + + WndClassEx wndClassEx = new() + { + cbSize = Marshal.SizeOf<WndClassEx>(), + hInstance = GetModuleHandle(null), + lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), + style = ClassStyles.CsOwndc, + lpszClassName = Marshal.StringToHGlobalUni(_className), + hCursor = CreateArrowCursor(), + }; + + RegisterClassEx(ref wndClassEx); + + WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WsChild, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + Marshal.FreeHGlobal(wndClassEx.lpszClassName); + + return new PlatformHandle(WindowHandle, "HWND"); + } + + [SupportedOSPlatform("macos")] + IPlatformHandle CreateMacOS() + { + // Create a new CAMetalLayer. + ObjectiveC.Object layerObject = new("CAMetalLayer"); + ObjectiveC.Object metalLayer = layerObject.GetFromMessage("alloc"); + metalLayer.SendMessage("init"); + + // Create a child NSView to render into. + ObjectiveC.Object nsViewObject = new("NSView"); + ObjectiveC.Object child = nsViewObject.GetFromMessage("alloc"); + child.SendMessage("init", new ObjectiveC.NSRect(0, 0, 0, 0)); + + // Make its renderer our metal layer. + child.SendMessage("setWantsLayer:", 1); + child.SendMessage("setLayer:", metalLayer); + metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor); + + // Ensure the scale factor is up to date. + _updateBoundsCallback = rect => + { + metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor); + }; + + IntPtr nsView = child.ObjPtr; + MetalLayer = metalLayer.ObjPtr; + NsView = nsView; + + return new PlatformHandle(nsView, "NSView"); + } + + [SupportedOSPlatform("Linux")] + void DestroyLinux() + { + X11Window?.Dispose(); + } + + [SupportedOSPlatform("windows")] + void DestroyWin32(IPlatformHandle handle) + { + DestroyWindow(handle.Handle); + UnregisterClass(_className, GetModuleHandle(null)); + } + + [SupportedOSPlatform("macos")] +#pragma warning disable CA1822 // Mark member as static + void DestroyMacOS() + { + // TODO + } +#pragma warning restore CA1822 + } +} |