diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs index 33192b51d..596df4c67 100644 --- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs +++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs @@ -1,19 +1,18 @@ -using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using Dalamud.Bindings.ImGui; +using Dalamud.Memory; using Serilog; using TerraFX.Interop.Windows; -using static Dalamud.Interface.ImGuiBackend.Helpers.ImGuiViewportHelpers; - using static TerraFX.Interop.Windows.Windows; using ERROR = TerraFX.Interop.Windows.ERROR; @@ -585,51 +584,50 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler private struct ViewportHandler : IDisposable { - [SuppressMessage("ReSharper", "CollectionNeverQueried.Local", Justification = "Keeping references alive")] - private readonly List delegateReferences = new(); + private static readonly string WindowClassName = typeof(ViewportHandler).FullName!; private Win32InputHandler input; - private nint classNamePtr; private bool wantUpdateMonitors = true; public ViewportHandler(Win32InputHandler input) { this.input = input; - this.classNamePtr = Marshal.StringToHGlobalUni("ImGui Platform"); var pio = ImGui.GetPlatformIO(); - pio.PlatformCreateWindow = this.RegisterFunctionPointer(this.OnCreateWindow); - pio.PlatformDestroyWindow = this.RegisterFunctionPointer(this.OnDestroyWindow); - pio.PlatformShowWindow = this.RegisterFunctionPointer(this.OnShowWindow); - pio.PlatformSetWindowPos = this.RegisterFunctionPointer(this.OnSetWindowPos); - pio.PlatformGetWindowPos = this.RegisterFunctionPointer(this.OnGetWindowPos); - pio.PlatformSetWindowSize = this.RegisterFunctionPointer(this.OnSetWindowSize); - pio.PlatformGetWindowSize = this.RegisterFunctionPointer(this.OnGetWindowSize); - pio.PlatformSetWindowFocus = this.RegisterFunctionPointer(this.OnSetWindowFocus); - pio.PlatformGetWindowFocus = this.RegisterFunctionPointer(this.OnGetWindowFocus); - pio.PlatformGetWindowMinimized = - this.RegisterFunctionPointer(this.OnGetWindowMinimized); - pio.PlatformSetWindowTitle = this.RegisterFunctionPointer(this.OnSetWindowTitle); - pio.PlatformSetWindowAlpha = this.RegisterFunctionPointer(this.OnSetWindowAlpha); - pio.PlatformUpdateWindow = this.RegisterFunctionPointer(this.OnUpdateWindow); + pio.PlatformCreateWindow = (delegate* unmanaged[Cdecl])&OnCreateWindow; + pio.PlatformDestroyWindow = (delegate* unmanaged[Cdecl])&OnDestroyWindow; + pio.PlatformShowWindow = (delegate* unmanaged[Cdecl])&OnShowWindow; + pio.PlatformSetWindowPos = (delegate* unmanaged[Cdecl])&OnSetWindowPos; + pio.PlatformGetWindowPos = (delegate* unmanaged[Cdecl])&OnGetWindowPos; + pio.PlatformSetWindowSize = (delegate* unmanaged[Cdecl])&OnSetWindowSize; + pio.PlatformGetWindowSize = (delegate* unmanaged[Cdecl])&OnGetWindowSize; + pio.PlatformSetWindowFocus = (delegate* unmanaged[Cdecl])&OnSetWindowFocus; + pio.PlatformGetWindowFocus = (delegate* unmanaged[Cdecl])&OnGetWindowFocus; + pio.PlatformGetWindowMinimized = (delegate* unmanaged[Cdecl])&OnGetWindowMinimized; + pio.PlatformSetWindowTitle = (delegate* unmanaged[Cdecl])&OnSetWindowTitle; + pio.PlatformSetWindowAlpha = (delegate* unmanaged[Cdecl])&OnSetWindowAlpha; + pio.PlatformUpdateWindow = (delegate* unmanaged[Cdecl])&OnUpdateWindow; // pio.Platform_SetImeInputPos = this.RegisterFunctionPointer(this.OnSetImeInputPos); // pio.Platform_GetWindowDpiScale = this.RegisterFunctionPointer(this.OnGetWindowDpiScale); // pio.Platform_ChangedViewport = this.RegisterFunctionPointer(this.OnChangedViewport); - var wcex = new WNDCLASSEXW + fixed (char* windowClassNamePtr = WindowClassName) { - cbSize = (uint)sizeof(WNDCLASSEXW), - style = CS.CS_HREDRAW | CS.CS_VREDRAW, - hInstance = GetModuleHandleW(null), - hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND), - lpfnWndProc = (delegate* unmanaged)Marshal - .GetFunctionPointerForDelegate(this.input.wndProcDelegate), - lpszClassName = (ushort*)this.classNamePtr, - }; + var wcex = new WNDCLASSEXW + { + cbSize = (uint)sizeof(WNDCLASSEXW), + style = CS.CS_HREDRAW | CS.CS_VREDRAW, + hInstance = (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module), + hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND), + lpfnWndProc = (delegate* unmanaged)Marshal + .GetFunctionPointerForDelegate(this.input.wndProcDelegate), + lpszClassName = (ushort*)windowClassNamePtr, + }; - if (RegisterClassExW(&wcex) == 0) - throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? new("RegisterClassEx Fail"); + if (RegisterClassExW(&wcex) == 0) + throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? new("RegisterClassEx Fail"); + } // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. @@ -657,11 +655,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler ImGui.GetPlatformIO().Handle->Monitors = default; } - if (this.classNamePtr != 0) + fixed (char* windowClassNamePtr = WindowClassName) { - UnregisterClassW((ushort*)this.classNamePtr, GetModuleHandleW(null)); - Marshal.FreeHGlobal(this.classNamePtr); - this.classNamePtr = 0; + UnregisterClassW( + (ushort*)windowClassNamePtr, + (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module)); } pio.PlatformCreateWindow = null; @@ -750,13 +748,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler } } - private void* RegisterFunctionPointer(T obj) - { - this.delegateReferences.Add(obj); - return Marshal.GetFunctionPointerForDelegate(obj).ToPointer(); - } - - private void OnCreateWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnCreateWindow(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)Marshal.AllocHGlobal(Marshal.SizeOf()); viewport.PlatformUserData = data; @@ -784,12 +777,12 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler }; AdjustWindowRectEx(&rect, (uint)data->DwStyle, false, (uint)data->DwExStyle); - fixed (char* pwszWindowTitle = "Untitled") + fixed (char* windowClassNamePtr = WindowClassName) { data->Hwnd = CreateWindowExW( (uint)data->DwExStyle, - (ushort*)this.classNamePtr, - (ushort*)pwszWindowTitle, + (ushort*)windowClassNamePtr, + (ushort*)windowClassNamePtr, (uint)data->DwStyle, rect.left, rect.top, @@ -797,8 +790,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler rect.bottom - rect.top, parentWindow, default, - GetModuleHandleW(null), - default); + (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module), + null); } data->HwndOwned = true; @@ -806,7 +799,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd; } - private void OnDestroyWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnDestroyWindow(ImGuiViewportPtr viewport) { // This is also called on the main viewport for some reason, and we never set that viewport's PlatformUserData if (viewport.PlatformUserData == null) return; @@ -817,7 +811,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler { // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event. ReleaseCapture(); - SetCapture(this.input.hWnd); + if (viewport.ParentViewportId != 0) + { + var parentViewport = ImGui.FindViewportByID(viewport.ParentViewportId); + SetCapture((HWND)parentViewport.PlatformHandle); + } } if (data->Hwnd != nint.Zero && data->HwndOwned) @@ -836,7 +834,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler viewport.PlatformUserData = viewport.PlatformHandle = null; } - private void OnShowWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnShowWindow(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; @@ -846,7 +845,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler ShowWindow(data->Hwnd, SW.SW_SHOW); } - private void OnUpdateWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnUpdateWindow(ImGuiViewportPtr viewport) { // (Optional) Update Win32 style if it changed _after_ creation. // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. @@ -907,17 +907,18 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler } } - private Vector2* OnGetWindowPos(Vector2* returnStorage, ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static Vector2* OnGetWindowPos(Vector2* returnValueStorage, ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var pt = new POINT { x = 0, y = 0 }; ClientToScreen(data->Hwnd, &pt); - returnStorage->X = pt.x; - returnStorage->Y = pt.y; - return returnStorage; + *returnValueStorage = new(pt.x, pt.y); + return returnValueStorage; } - private void OnSetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var rect = new RECT((int)pos.X, (int)pos.Y, (int)pos.X, (int)pos.Y); @@ -934,17 +935,18 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler SWP.SWP_NOACTIVATE); } - private Vector2* OnGetWindowSize(Vector2* returnStorage, ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static Vector2* OnGetWindowSize(Vector2* returnValueStorage, ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; RECT rect; GetClientRect(data->Hwnd, &rect); - returnStorage->X = rect.right - rect.left; - returnStorage->Y = rect.bottom - rect.top; - return returnStorage; + *returnValueStorage = new(rect.right - rect.left, rect.bottom - rect.top); + return returnValueStorage; } - private void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; @@ -962,7 +964,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler SWP.SWP_NOACTIVATE); } - private void OnSetWindowFocus(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowFocus(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; @@ -971,26 +974,30 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler SetFocus(data->Hwnd); } - private bool OnGetWindowFocus(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static byte OnGetWindowFocus(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; - return GetForegroundWindow() == data->Hwnd; + return GetForegroundWindow() == data->Hwnd ? (byte)1 : (byte)0; } - private bool OnGetWindowMinimized(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static byte OnGetWindowMinimized(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; - return IsIconic(data->Hwnd); + return IsIconic(data->Hwnd) ? (byte)1 : (byte)0; } - private void OnSetWindowTitle(ImGuiViewportPtr viewport, string title) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowTitle(ImGuiViewportPtr viewport, byte* title) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; - fixed (char* pwszTitle = title) + fixed (char* pwszTitle = MemoryHelper.ReadStringNullTerminated((nint)title)) SetWindowTextW(data->Hwnd, (ushort*)pwszTitle); } - private void OnSetWindowAlpha(ImGuiViewportPtr viewport, float alpha) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowAlpha(ImGuiViewportPtr viewport, float alpha) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var style = GetWindowLongW(data->Hwnd, GWL.GWL_EXSTYLE);