From 4bf0ddf5ffe78448177a525ffa65c1f9412b73f4 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Sun, 13 Apr 2025 17:45:46 +0200 Subject: [PATCH] Document ImGui multimonitor not working and other possible pitfalls --- CustomResolution2782/Hooks/CursorPosHooks.cs | 50 +++++++++++++++++-- CustomResolution2782/Hooks/WindowRectHooks.cs | 7 ++- CustomResolution2782/Plugin.cs | 10 ++-- README.md | 27 ++++++++++ 4 files changed, 84 insertions(+), 10 deletions(-) diff --git a/CustomResolution2782/Hooks/CursorPosHooks.cs b/CustomResolution2782/Hooks/CursorPosHooks.cs index 898c1d4..4a8c27a 100644 --- a/CustomResolution2782/Hooks/CursorPosHooks.cs +++ b/CustomResolution2782/Hooks/CursorPosHooks.cs @@ -10,6 +10,8 @@ public sealed unsafe class CursorPosHooks : IDisposable { private readonly Hook _getCursorPosHook; private readonly Hook _setCursorPosHook; + private readonly Hook _screenToClientHook; + private readonly Hook _clientToScreenHook; public CursorPosHooks() { @@ -18,6 +20,12 @@ public sealed unsafe class CursorPosHooks : IDisposable _setCursorPosHook = Service.GameInteropProvider.HookFromSymbol("user32.dll", "SetCursorPos", SetCursorPosDetour); _setCursorPosHook.Enable(); + + _screenToClientHook = Service.GameInteropProvider.HookFromSymbol("user32.dll", "ScreenToClient", ScreenToClientDetour); + _screenToClientHook.Enable(); + + _clientToScreenHook = Service.GameInteropProvider.HookFromSymbol("user32.dll", "ClientToScreen", ClientToScreenDetour); + _clientToScreenHook.Enable(); } [UnmanagedFunctionPointer(CallingConvention.StdCall)] @@ -26,19 +34,46 @@ public sealed unsafe class CursorPosHooks : IDisposable [UnmanagedFunctionPointer(CallingConvention.StdCall)] private unsafe delegate bool SetCursorPosDelegate(int x, int y); + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private unsafe delegate bool ScreenToClientDelegate(HWND hWnd, POINT* lpPoint); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private unsafe delegate bool ClientToScreenDelegate(HWND hWnd, POINT* lpPoint); + public void Dispose() { _getCursorPosHook.Dispose(); _setCursorPosHook.Dispose(); + _screenToClientHook.Dispose(); + _clientToScreenHook.Dispose(); + } + + public bool GetCursorPosOrig(POINT* lpPoint) + { + return _getCursorPosHook.Original(lpPoint); + } + + public bool SetCursorPosOrig(int x, int y) + { + return _setCursorPosHook.Original(x, y); + } + + public bool ScreenToClientOrig(HWND hWnd, POINT* lpPoint) + { + return _screenToClientHook.Original(hWnd, lpPoint); + } + + public bool ClientToScreenOrig(HWND hWnd, POINT* lpPoint) + { + return _clientToScreenHook.Original(hWnd, lpPoint); } private bool GetCursorPosDetour(POINT* lpPoint) { var rv = _getCursorPosHook.Original(lpPoint); - - if (!rv) + if (!rv || lpPoint == null) { - return false; + return rv; } #if false @@ -69,4 +104,13 @@ public sealed unsafe class CursorPosHooks : IDisposable return _setCursorPosHook.Original(x, y); } + private bool ScreenToClientDetour(HWND hWnd, POINT* lpPoint) + { + return _screenToClientHook.Original(hWnd, lpPoint); + } + + private bool ClientToScreenDetour(HWND hWnd, POINT* lpPoint) + { + return _clientToScreenHook.Original(hWnd, lpPoint); + } } diff --git a/CustomResolution2782/Hooks/WindowRectHooks.cs b/CustomResolution2782/Hooks/WindowRectHooks.cs index 072c555..0124cb4 100644 --- a/CustomResolution2782/Hooks/WindowRectHooks.cs +++ b/CustomResolution2782/Hooks/WindowRectHooks.cs @@ -50,7 +50,10 @@ public sealed unsafe class WindowRectHooks : IDisposable Service.PluginLog.Debug($"GetClientRectDetour A @ {lpRect->left} {lpRect->top} {lpRect->right} {lpRect->bottom}"); #endif - Service.Plugin.ConvertCoordsWinToGame(ref lpRect->right, ref lpRect->bottom); + if (hWnd == Service.Plugin.CurrentHWND) + { + Service.Plugin.ConvertCoordsWinToGame(ref lpRect->right, ref lpRect->bottom); + } #if false Service.PluginLog.Debug($"GetClientRectDetour B @ {lpRect->left} {lpRect->top} {lpRect->right} {lpRect->bottom}"); @@ -65,7 +68,7 @@ public sealed unsafe class WindowRectHooks : IDisposable Service.PluginLog.Debug($"SetWindowPosDetour A @ {X} {Y} {cx} {cy}"); #endif - if (Service.DebugConfig.SetSizeMode == SetSizeMode.LegacyHookSetWindowPos) + if (Service.DebugConfig.SetSizeMode == SetSizeMode.LegacyHookSetWindowPos && hWnd == Service.Plugin.CurrentHWND) { Service.Plugin.ConvertCoordsGameToWin(ref cx, ref cy); } diff --git a/CustomResolution2782/Plugin.cs b/CustomResolution2782/Plugin.cs index a9d00e4..b14ce7c 100644 --- a/CustomResolution2782/Plugin.cs +++ b/CustomResolution2782/Plugin.cs @@ -65,7 +65,7 @@ public sealed unsafe class Plugin : IDalamudPlugin public string Name => "CustomResolution"; - public HWND CurrentHWND { get; private set; } + public HWND CurrentHWND => _currentHwnd; public uint CurrentWidth { get; private set; } public uint CurrentHeight { get; private set; } @@ -140,12 +140,12 @@ public sealed unsafe class Plugin : IDalamudPlugin var p = new POINT(x, y); - ScreenToClient(_currentHwnd, &p); + Service.CursorPosHooks.ScreenToClientOrig(_currentHwnd, &p); p.x = (int) Math.Round(p.x * scaleX); p.y = (int) Math.Round(p.y * scaleY); - ClientToScreen(_currentHwnd, &p); + Service.CursorPosHooks.ClientToScreenOrig(_currentHwnd, &p); x = p.x; y = p.y; @@ -163,12 +163,12 @@ public sealed unsafe class Plugin : IDalamudPlugin var p = new POINT(x, y); - ScreenToClient(_currentHwnd, &p); + Service.CursorPosHooks.ScreenToClientOrig(_currentHwnd, &p); p.x = (int) Math.Round(p.x * scaleX); p.y = (int) Math.Round(p.y * scaleY); - ClientToScreen(_currentHwnd, &p); + Service.CursorPosHooks.ClientToScreenOrig(_currentHwnd, &p); x = p.x; y = p.y; diff --git a/README.md b/README.md index ab1e3ee..499206c 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,30 @@ Enforces a custom resolution for the game, similar to NVIDIA DSR. Mirrored at https://gitlab.com/0x0ade/dp-customresolution in case you want to submit a merge request. Please do ping me on Discord though (I'm in the Dalamud server) as some notifs might fly under my radar. + +# Troubleshooting + +
My multi-monitor windows aren't working correctly! +Similarly to some other plugins (f.e. Umbra), multi-monitor mode in Dalamud might not work correctly. +In the case of cres, fixing this would be a huge undertaking to make ImGui handle the cursor inside and outside +the main game window differently, and to also handle window positioning correctly (spoiler: there is not one way +to handle it correctly - things are already shifting around when just resizing the game normally). +
+ +
The game UI is really really small! I don't want to scale all my HUD manually! +Good news: You don't need to! XIV itself has got a "High Resolution UI Settings" scaling option in the system +configuration window, in the very first category ("Display Settings"), right under the resolution options. + +Bad news: We're currently limited to the very options that XIV gives us. Perhaps a deeper dive into how the game +renders the HUD and decoupling that from the actual game render resolution could help, but it's on the same +scale of difficulty as fixing multi-monitor windows. +
+ +
I'm scaling by 3x or higher, but I'm still getting jagged edges! +The plugin currently only gives FFXIV a fake resolution to work with internally, but it doesn't ensure that the +extra pixels land on your screen - those will still be used for in-game or ReShade screenshots though. + +Windows should perform basic filtering with 2x scaling (it might not work on some graphics cards?), and I haven't +heard back from anyone using this on Linux yet. But an universal fix for this is possible - it just requires more +time than I have on my hands right now. +