Document ImGui multimonitor not working and other possible pitfalls

This commit is contained in:
Jade Macho 2025-04-13 17:45:46 +02:00
parent ada9113b37
commit 4bf0ddf5ff
Signed by: 0x0ade
GPG key ID: E1960710FE4FBEEF
4 changed files with 84 additions and 10 deletions

View file

@ -10,6 +10,8 @@ public sealed unsafe class CursorPosHooks : IDisposable
{
private readonly Hook<GetCursorPosDelegate> _getCursorPosHook;
private readonly Hook<SetCursorPosDelegate> _setCursorPosHook;
private readonly Hook<ScreenToClientDelegate> _screenToClientHook;
private readonly Hook<ClientToScreenDelegate> _clientToScreenHook;
public CursorPosHooks()
{
@ -18,6 +20,12 @@ public sealed unsafe class CursorPosHooks : IDisposable
_setCursorPosHook = Service.GameInteropProvider.HookFromSymbol<SetCursorPosDelegate>("user32.dll", "SetCursorPos", SetCursorPosDetour);
_setCursorPosHook.Enable();
_screenToClientHook = Service.GameInteropProvider.HookFromSymbol<ScreenToClientDelegate>("user32.dll", "ScreenToClient", ScreenToClientDetour);
_screenToClientHook.Enable();
_clientToScreenHook = Service.GameInteropProvider.HookFromSymbol<ClientToScreenDelegate>("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);
}
}

View file

@ -50,7 +50,10 @@ public sealed unsafe class WindowRectHooks : IDisposable
Service.PluginLog.Debug($"GetClientRectDetour A @ {lpRect->left} {lpRect->top} {lpRect->right} {lpRect->bottom}");
#endif
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);
}

View file

@ -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;

View file

@ -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
<details><summary>My multi-monitor windows aren't working correctly!</summary>
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).
</details>
<details><summary>The game UI is really really small! I don't want to scale all my HUD manually!</summary>
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.
</details>
<details><summary>I'm scaling by 3x or higher, but I'm still getting jagged edges!</summary>
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.
</details>