2023-01-16 00:14:01 +00:00
|
|
|
using Avalonia;
|
|
|
|
using Avalonia.Controls;
|
|
|
|
using Avalonia.Input;
|
|
|
|
using Avalonia.Platform;
|
|
|
|
using Ryujinx.Common.Configuration;
|
|
|
|
using Ryujinx.Ui.Common.Configuration;
|
2023-02-09 03:08:15 +00:00
|
|
|
using Ryujinx.Ui.Common.Helper;
|
2023-01-16 00:14:01 +00:00
|
|
|
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; }
|
|
|
|
|
2023-02-09 03:08:15 +00:00
|
|
|
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
|
2023-01-16 00:14:01 +00:00
|
|
|
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
|
|
|
|
|
|
|
|
public event EventHandler<IntPtr> WindowCreated;
|
|
|
|
public event EventHandler<Size> SizeChanged;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
SizeChanged?.Invoke(this, rect.Size);
|
|
|
|
_updateBoundsCallback?.Invoke(rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle control)
|
|
|
|
{
|
|
|
|
if (OperatingSystem.IsLinux())
|
|
|
|
{
|
|
|
|
return CreateLinux(control);
|
|
|
|
}
|
|
|
|
else if (OperatingSystem.IsWindows())
|
|
|
|
{
|
|
|
|
return CreateWin32(control);
|
|
|
|
}
|
|
|
|
else if (OperatingSystem.IsMacOS())
|
|
|
|
{
|
2023-01-16 02:59:41 +00:00
|
|
|
return CreateMacOS();
|
2023-01-16 00:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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")]
|
2023-01-16 02:59:41 +00:00
|
|
|
private IPlatformHandle CreateLinux(IPlatformHandle control)
|
2023-01-16 00:14:01 +00:00
|
|
|
{
|
|
|
|
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
|
|
|
|
{
|
|
|
|
X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
|
2023-01-16 02:59:41 +00:00
|
|
|
X11Window.Hide();
|
2023-01-16 00:14:01 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-11 16:31:22 +00:00
|
|
|
X11Window = PlatformHelper.CreateOpenGLWindow(new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false), 0, 0, 100, 100) as GLXWindow;
|
2023-01-16 00:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2023-01-22 00:42:55 +00:00
|
|
|
if (msg == WindowsMessages.LBUTTONDOWN ||
|
|
|
|
msg == WindowsMessages.RBUTTONDOWN ||
|
|
|
|
msg == WindowsMessages.LBUTTONUP ||
|
|
|
|
msg == WindowsMessages.RBUTTONUP ||
|
|
|
|
msg == WindowsMessages.MOUSEMOVE)
|
2023-01-16 00:14:01 +00:00
|
|
|
{
|
2023-01-22 00:42:55 +00:00
|
|
|
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
|
|
|
|
Pointer pointer = new(0, PointerType.Mouse, true);
|
|
|
|
|
|
|
|
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,
|
|
|
|
VisualRoot,
|
|
|
|
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,
|
|
|
|
VisualRoot,
|
|
|
|
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,
|
|
|
|
VisualRoot,
|
|
|
|
rootVisualPosition,
|
|
|
|
(ulong)Environment.TickCount64,
|
|
|
|
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
|
|
|
|
KeyModifiers.None);
|
|
|
|
|
|
|
|
RaiseEvent(evnt);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-01-16 00:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
|
|
};
|
|
|
|
|
|
|
|
WNDCLASSEX wndClassEx = new()
|
|
|
|
{
|
|
|
|
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
|
|
|
|
hInstance = GetModuleHandle(null),
|
|
|
|
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
|
|
|
|
style = ClassStyles.CS_OWNDC,
|
|
|
|
lpszClassName = Marshal.StringToHGlobalUni(_className),
|
|
|
|
hCursor = CreateArrowCursor()
|
|
|
|
};
|
|
|
|
|
|
|
|
RegisterClassEx(ref wndClassEx);
|
|
|
|
|
|
|
|
WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
|
|
|
|
|
|
|
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
|
|
|
|
|
|
|
|
return new PlatformHandle(WindowHandle, "HWND");
|
|
|
|
}
|
|
|
|
|
|
|
|
[SupportedOSPlatform("macos")]
|
2023-01-16 02:59:41 +00:00
|
|
|
IPlatformHandle CreateMacOS()
|
2023-01-16 00:14:01 +00:00
|
|
|
{
|
2023-02-09 03:08:15 +00:00
|
|
|
// Create a new CAMetalLayer.
|
2023-06-29 00:39:22 +00:00
|
|
|
ObjectiveC.Object layerObject = new("CAMetalLayer");
|
|
|
|
ObjectiveC.Object metalLayer = layerObject.GetFromMessage("alloc");
|
|
|
|
metalLayer.SendMessage("init");
|
2023-02-09 03:08:15 +00:00
|
|
|
|
|
|
|
// Create a child NSView to render into.
|
2023-06-29 00:39:22 +00:00
|
|
|
ObjectiveC.Object nsViewObject = new("NSView");
|
|
|
|
ObjectiveC.Object child = nsViewObject.GetFromMessage("alloc");
|
|
|
|
child.SendMessage("init", new ObjectiveC.NSRect(0, 0, 0, 0));
|
2023-02-09 03:08:15 +00:00
|
|
|
|
|
|
|
// Make its renderer our metal layer.
|
2023-06-29 00:39:22 +00:00
|
|
|
child.SendMessage("setWantsLayer:", 1);
|
|
|
|
child.SendMessage("setLayer:", metalLayer);
|
|
|
|
metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor);
|
2023-02-09 03:08:15 +00:00
|
|
|
|
|
|
|
// Ensure the scale factor is up to date.
|
|
|
|
_updateBoundsCallback = rect =>
|
|
|
|
{
|
2023-06-29 00:39:22 +00:00
|
|
|
metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor);
|
2023-02-09 03:08:15 +00:00
|
|
|
};
|
2023-01-16 00:14:01 +00:00
|
|
|
|
2023-06-29 00:39:22 +00:00
|
|
|
IntPtr nsView = child.ObjPtr;
|
|
|
|
MetalLayer = metalLayer.ObjPtr;
|
2023-01-16 00:14:01 +00:00
|
|
|
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")]
|
|
|
|
void DestroyMacOS()
|
|
|
|
{
|
2023-02-09 03:08:15 +00:00
|
|
|
// TODO
|
2023-01-16 00:14:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|