2022-09-19 18:05:26 +00:00
|
|
|
using Avalonia;
|
|
|
|
using Avalonia.Controls;
|
|
|
|
using Avalonia.Input;
|
|
|
|
using Avalonia.Platform;
|
2022-12-29 14:24:05 +00:00
|
|
|
using Ryujinx.Ava.UI.Helper;
|
2022-09-19 18:05:26 +00:00
|
|
|
using SPB.Graphics;
|
|
|
|
using SPB.Platform;
|
|
|
|
using SPB.Platform.GLX;
|
|
|
|
using System;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using System.Runtime.Versioning;
|
|
|
|
using System.Threading.Tasks;
|
2022-12-29 14:24:05 +00:00
|
|
|
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
2022-09-19 18:05:26 +00:00
|
|
|
|
2022-12-29 14:24:05 +00:00
|
|
|
namespace Ryujinx.Ava.UI.Helpers
|
2022-09-19 18:05:26 +00:00
|
|
|
{
|
2022-11-26 11:06:53 +00:00
|
|
|
public class EmbeddedWindow : NativeControlHost
|
2022-09-19 18:05:26 +00:00
|
|
|
{
|
|
|
|
private WindowProc _wndProcDelegate;
|
|
|
|
private string _className;
|
|
|
|
|
2022-11-26 11:06:53 +00:00
|
|
|
protected GLXWindow X11Window { get; set; }
|
2022-09-19 18:05:26 +00:00
|
|
|
protected IntPtr WindowHandle { get; set; }
|
|
|
|
protected IntPtr X11Display { get; set; }
|
2022-12-06 22:00:25 +00:00
|
|
|
protected IntPtr NsView { get; set; }
|
|
|
|
protected IntPtr MetalLayer { get; set; }
|
|
|
|
|
|
|
|
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
2022-09-19 18:05:26 +00:00
|
|
|
|
|
|
|
public event EventHandler<IntPtr> WindowCreated;
|
|
|
|
public event EventHandler<Size> SizeChanged;
|
|
|
|
|
|
|
|
protected virtual void OnWindowDestroyed() { }
|
|
|
|
protected virtual void OnWindowDestroying()
|
|
|
|
{
|
|
|
|
WindowHandle = IntPtr.Zero;
|
|
|
|
X11Display = IntPtr.Zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
public EmbeddedWindow()
|
|
|
|
{
|
2022-12-10 20:21:13 +00:00
|
|
|
var stateObserverable = this.GetObservable(BoundsProperty);
|
2022-09-19 18:05:26 +00:00
|
|
|
|
|
|
|
stateObserverable.Subscribe(StateChanged);
|
|
|
|
|
|
|
|
this.Initialized += NativeEmbeddedWindow_Initialized;
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual void OnWindowCreated() { }
|
|
|
|
|
|
|
|
private void NativeEmbeddedWindow_Initialized(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
OnWindowCreated();
|
|
|
|
|
|
|
|
Task.Run(() =>
|
|
|
|
{
|
|
|
|
WindowCreated?.Invoke(this, WindowHandle);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void StateChanged(Rect rect)
|
|
|
|
{
|
|
|
|
SizeChanged?.Invoke(this, rect.Size);
|
2022-12-06 22:00:25 +00:00
|
|
|
_updateBoundsCallback?.Invoke(rect);
|
2022-09-19 18:05:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
|
|
|
|
{
|
|
|
|
if (OperatingSystem.IsLinux())
|
|
|
|
{
|
|
|
|
return CreateLinux(parent);
|
|
|
|
}
|
|
|
|
else if (OperatingSystem.IsWindows())
|
|
|
|
{
|
|
|
|
return CreateWin32(parent);
|
|
|
|
}
|
2022-12-06 22:00:25 +00:00
|
|
|
else if (OperatingSystem.IsMacOS())
|
|
|
|
{
|
|
|
|
return CreateMacOs(parent);
|
|
|
|
}
|
|
|
|
|
2022-09-19 18:05:26 +00:00
|
|
|
return base.CreateNativeControlCore(parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void DestroyNativeControlCore(IPlatformHandle control)
|
|
|
|
{
|
|
|
|
OnWindowDestroying();
|
|
|
|
|
|
|
|
if (OperatingSystem.IsLinux())
|
|
|
|
{
|
|
|
|
DestroyLinux();
|
|
|
|
}
|
|
|
|
else if (OperatingSystem.IsWindows())
|
|
|
|
{
|
|
|
|
DestroyWin32(control);
|
|
|
|
}
|
2022-12-06 22:00:25 +00:00
|
|
|
else if (OperatingSystem.IsMacOS())
|
|
|
|
{
|
|
|
|
DestroyMacOS();
|
|
|
|
}
|
2022-09-19 18:05:26 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
base.DestroyNativeControlCore(control);
|
|
|
|
}
|
|
|
|
|
|
|
|
OnWindowDestroyed();
|
|
|
|
}
|
|
|
|
|
|
|
|
[SupportedOSPlatform("linux")]
|
2022-11-26 11:06:53 +00:00
|
|
|
protected virtual IPlatformHandle CreateLinux(IPlatformHandle parent)
|
2022-09-19 18:05:26 +00:00
|
|
|
{
|
2022-11-26 11:06:53 +00:00
|
|
|
X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
|
2022-09-19 18:05:26 +00:00
|
|
|
WindowHandle = X11Window.WindowHandle.RawHandle;
|
2022-11-26 11:06:53 +00:00
|
|
|
X11Display = X11Window.DisplayHandle.RawHandle;
|
2022-11-25 17:40:44 +00:00
|
|
|
|
2022-09-19 18:05:26 +00:00
|
|
|
return new PlatformHandle(WindowHandle, "X11");
|
|
|
|
}
|
|
|
|
|
|
|
|
[SupportedOSPlatform("windows")]
|
2022-11-26 11:06:53 +00:00
|
|
|
IPlatformHandle CreateWin32(IPlatformHandle parent)
|
2022-09-19 18:05:26 +00:00
|
|
|
{
|
|
|
|
_className = "NativeWindow-" + Guid.NewGuid();
|
|
|
|
_wndProcDelegate = WndProc;
|
|
|
|
var wndClassEx = new WNDCLASSEX
|
|
|
|
{
|
|
|
|
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
|
|
|
|
hInstance = GetModuleHandle(null),
|
2022-12-15 17:07:31 +00:00
|
|
|
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
|
2022-09-19 18:05:26 +00:00
|
|
|
style = ClassStyles.CS_OWNDC,
|
2022-12-15 17:07:31 +00:00
|
|
|
lpszClassName = Marshal.StringToHGlobalUni(_className),
|
2022-09-19 18:05:26 +00:00
|
|
|
hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW)
|
|
|
|
};
|
|
|
|
|
|
|
|
var atom = RegisterClassEx(ref wndClassEx);
|
|
|
|
|
|
|
|
var handle = CreateWindowEx(
|
|
|
|
0,
|
|
|
|
_className,
|
|
|
|
"NativeWindow",
|
|
|
|
WindowStyles.WS_CHILD,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
640,
|
|
|
|
480,
|
|
|
|
parent.Handle,
|
|
|
|
IntPtr.Zero,
|
|
|
|
IntPtr.Zero,
|
|
|
|
IntPtr.Zero);
|
|
|
|
|
|
|
|
WindowHandle = handle;
|
2022-12-15 17:07:31 +00:00
|
|
|
|
|
|
|
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
|
|
|
|
|
2022-09-19 18:05:26 +00:00
|
|
|
return new PlatformHandle(WindowHandle, "HWND");
|
|
|
|
}
|
|
|
|
|
|
|
|
[SupportedOSPlatform("windows")]
|
2022-11-26 11:06:53 +00:00
|
|
|
IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
|
2022-09-19 18:05:26 +00:00
|
|
|
{
|
|
|
|
var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF);
|
|
|
|
var root = VisualRoot as Window;
|
|
|
|
bool isLeft = false;
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WindowsMessages.LBUTTONDOWN:
|
|
|
|
case WindowsMessages.RBUTTONDOWN:
|
|
|
|
isLeft = msg == WindowsMessages.LBUTTONDOWN;
|
|
|
|
this.RaiseEvent(new PointerPressedEventArgs(
|
|
|
|
this,
|
2022-12-10 20:21:13 +00:00
|
|
|
new Pointer(0, PointerType.Mouse, true),
|
2022-09-19 18:05:26 +00:00
|
|
|
root,
|
|
|
|
this.TranslatePoint(point, root).Value,
|
|
|
|
(ulong)Environment.TickCount64,
|
|
|
|
new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed),
|
|
|
|
KeyModifiers.None));
|
|
|
|
break;
|
|
|
|
case WindowsMessages.LBUTTONUP:
|
|
|
|
case WindowsMessages.RBUTTONUP:
|
|
|
|
isLeft = msg == WindowsMessages.LBUTTONUP;
|
|
|
|
this.RaiseEvent(new PointerReleasedEventArgs(
|
|
|
|
this,
|
2022-12-10 20:21:13 +00:00
|
|
|
new Pointer(0, PointerType.Mouse, true),
|
2022-09-19 18:05:26 +00:00
|
|
|
root,
|
|
|
|
this.TranslatePoint(point, root).Value,
|
|
|
|
(ulong)Environment.TickCount64,
|
|
|
|
new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased),
|
|
|
|
KeyModifiers.None,
|
|
|
|
isLeft ? MouseButton.Left : MouseButton.Right));
|
|
|
|
break;
|
|
|
|
case WindowsMessages.MOUSEMOVE:
|
|
|
|
this.RaiseEvent(new PointerEventArgs(
|
|
|
|
PointerMovedEvent,
|
|
|
|
this,
|
2022-12-10 20:21:13 +00:00
|
|
|
new Pointer(0, PointerType.Mouse, true),
|
2022-09-19 18:05:26 +00:00
|
|
|
root,
|
|
|
|
this.TranslatePoint(point, root).Value,
|
|
|
|
(ulong)Environment.TickCount64,
|
|
|
|
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
|
|
|
|
KeyModifiers.None));
|
|
|
|
break;
|
|
|
|
}
|
2022-12-29 14:24:05 +00:00
|
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
2022-09-19 18:05:26 +00:00
|
|
|
}
|
|
|
|
|
2022-12-06 22:00:25 +00:00
|
|
|
[SupportedOSPlatform("macos")]
|
|
|
|
IPlatformHandle CreateMacOs(IPlatformHandle parent)
|
|
|
|
{
|
|
|
|
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
|
|
|
|
|
|
|
|
NsView = nsView;
|
|
|
|
|
|
|
|
return new PlatformHandle(nsView, "NSView");
|
|
|
|
}
|
|
|
|
|
2022-09-19 18:05:26 +00:00
|
|
|
void DestroyLinux()
|
|
|
|
{
|
|
|
|
X11Window?.Dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
[SupportedOSPlatform("windows")]
|
|
|
|
void DestroyWin32(IPlatformHandle handle)
|
|
|
|
{
|
|
|
|
DestroyWindow(handle.Handle);
|
|
|
|
UnregisterClass(_className, GetModuleHandle(null));
|
|
|
|
}
|
2022-12-06 22:00:25 +00:00
|
|
|
|
|
|
|
[SupportedOSPlatform("macos")]
|
|
|
|
void DestroyMacOS()
|
|
|
|
{
|
|
|
|
MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
|
|
|
|
}
|
2022-09-19 18:05:26 +00:00
|
|
|
}
|
|
|
|
}
|