From 78ff318397a3917b0c02063756b449a1d2cdc3d1 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Sat, 28 Jun 2025 21:38:35 +0200 Subject: [PATCH] core: Send WM_ resize msgs instead of requesting device resize --- CustomResolution2782/Cmds/MainCmd.cs | 4 +- CustomResolution2782/DebugConfiguration.cs | 12 ++- CustomResolution2782/Hooks/WindowRectHooks.cs | 7 +- CustomResolution2782/Hooks/WndProcHook.cs | 16 ++-- CustomResolution2782/Plugin.cs | 95 +++++++++++++++---- README.md | 6 +- 6 files changed, 107 insertions(+), 33 deletions(-) diff --git a/CustomResolution2782/Cmds/MainCmd.cs b/CustomResolution2782/Cmds/MainCmd.cs index 054c300..68b7ce2 100644 --- a/CustomResolution2782/Cmds/MainCmd.cs +++ b/CustomResolution2782/Cmds/MainCmd.cs @@ -79,12 +79,12 @@ public sealed class MainCmd : Cmd return; case "debugsizeold": - Service.DebugConfig.SetSizeMode = SetSizeMode.LegacyHookSetWindowPos; + Service.DebugConfig.ForceSizeMode = ForceSizeMode.LegacyRequestResolutionChange; Service.PrintChat("Switched to legacy size mode. Please report your use case / any bugs with the new mode to 0x0ade."); return; case "debugsizenew": - Service.DebugConfig.SetSizeMode = SetSizeMode.InterceptSystemConfig; + Service.DebugConfig.ForceSizeMode = ForceSizeMode.FakeWindowResize; Service.PrintChat("Switched back to new size mode."); return; } diff --git a/CustomResolution2782/DebugConfiguration.cs b/CustomResolution2782/DebugConfiguration.cs index 1abd16e..b353d92 100644 --- a/CustomResolution2782/DebugConfiguration.cs +++ b/CustomResolution2782/DebugConfiguration.cs @@ -8,13 +8,15 @@ public class DebugConfiguration { public bool IsDebug { get; set; } = false; - public SetSizeMode SetSizeMode { get; set; } = SetSizeMode.InterceptSystemConfig; + public SetWindowSizeMode SetWindowSizeMode { get; set; } = SetWindowSizeMode.InterceptSystemConfig; + + public ForceSizeMode ForceSizeMode { get; set; } = ForceSizeMode.FakeWindowResize; } -public enum SetSizeMode +public enum SetWindowSizeMode { /// - /// Intercept the system config width / height and scale it, but don't intercept window size sets. + /// Intercept the system config width / height and scale it to the window size, and don't intercept window size sets. /// Has got a mild risk of growing / shrinking windows if any codepath tries to get->set the window size, /// most notably with other plugins trying to change the window size. You shouldn't mix and match those anyway tho... /// ... and even then, I'm probably the only one who's going to read this and care about this. -jade @@ -22,8 +24,10 @@ public enum SetSizeMode InterceptSystemConfig = 0, /// - /// Legacy mode, went through more testing, works well except for the game getting confused about windowed mode size. + /// Legacy mode, went through more initial testing, works well except for the game getting confused about windowed mode size. /// Might revert or remove depending on how InterceptSystemConfig testing / fixups proceed. + /// + /// Hook SetWindowPos and turn the wanted size from scaled to window size on the fly. /// LegacyHookSetWindowPos = 1 } diff --git a/CustomResolution2782/Hooks/WindowRectHooks.cs b/CustomResolution2782/Hooks/WindowRectHooks.cs index 0124cb4..1f96132 100644 --- a/CustomResolution2782/Hooks/WindowRectHooks.cs +++ b/CustomResolution2782/Hooks/WindowRectHooks.cs @@ -62,13 +62,18 @@ public sealed unsafe class WindowRectHooks : IDisposable return rv; } + public bool SetWindowPosOrig(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags) + { + return _setWindowPosHook.Original(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); + } + private bool SetWindowPosDetour(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags) { #if false Service.PluginLog.Debug($"SetWindowPosDetour A @ {X} {Y} {cx} {cy}"); #endif - if (Service.DebugConfig.SetSizeMode == SetSizeMode.LegacyHookSetWindowPos && hWnd == Service.Plugin.CurrentHWND) + if (Service.DebugConfig.SetWindowSizeMode == SetWindowSizeMode.LegacyHookSetWindowPos && hWnd == Service.Plugin.CurrentHWND) { Service.Plugin.ConvertCoordsGameToWin(ref cx, ref cy); } diff --git a/CustomResolution2782/Hooks/WndProcHook.cs b/CustomResolution2782/Hooks/WndProcHook.cs index 37ad5d6..2e6417d 100644 --- a/CustomResolution2782/Hooks/WndProcHook.cs +++ b/CustomResolution2782/Hooks/WndProcHook.cs @@ -74,32 +74,32 @@ public sealed unsafe class WndProcHook : IDisposable } } - private static void ParamToCoords(LPARAM param, out int x, out int y) + public static void ParamToCoords(LPARAM param, out int x, out int y) { x = (short) (ushort) (param.Value & 0xFFFF); - y = (short) (ushort) (param.Value >> 16 & 0xFFFF); + y = (short) (ushort) ((param.Value >> 16) & 0xFFFF); } - private static LPARAM CoordsToParam(int x, int y) + public static LPARAM CoordsToParam(int x, int y) { nint value = 0; value |= ((ushort) (short) x) & 0xFFFF; - value |= ((ushort) (short) y) >> 16 & 0xFFFF; + value |= (((ushort) (short) y) & 0xFFFF) << 16; return value; } - private static void ParamToSize(LPARAM param, out uint x, out uint y) + public static void ParamToSize(LPARAM param, out uint x, out uint y) { x = (ushort) (param.Value & 0xFFFF); - y = (ushort) (param.Value >> 16 & 0xFFFF); + y = (ushort) ((param.Value >> 16) & 0xFFFF); } - private static LPARAM SizeToParam(uint x, uint y) + public static LPARAM SizeToParam(uint x, uint y) { nint value = 0; value |= ((ushort) x) & 0xFFFF; - value |= ((ushort) y) >> 16 & 0xFFFF; + value |= (((ushort) y) & 0xFFFF) << 16; return value; } diff --git a/CustomResolution2782/Plugin.cs b/CustomResolution2782/Plugin.cs index 92ef9ae..c83a862 100644 --- a/CustomResolution2782/Plugin.cs +++ b/CustomResolution2782/Plugin.cs @@ -1,4 +1,5 @@ -using Dalamud.Game.ClientState.Keys; +using CustomResolution.Hooks; +using Dalamud.Game.ClientState.Keys; using Dalamud.Game.Config; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -29,6 +30,8 @@ public sealed unsafe class Plugin : IDalamudPlugin private RECT _currentClientRect; private DXVKDWMHackMode _currentDXVKDWMHackMode = DXVKDWMHackMode.Off; private bool _ignoreConfigChanges = false; + private bool _currentlyFakeResize = false; + private bool _requestedResolutionChange = false; public Plugin(IDalamudPluginInterface pluginInterface) { @@ -226,14 +229,9 @@ public sealed unsafe class Plugin : IDalamudPlugin return; } - uint width, height; - var enabled = !_unloading && Service.Config.IsEnabled; - if (_wasEnabled != enabled) - { - Service.PluginLog.Info($"Changing state to: {enabled}"); - _wasEnabled = enabled; - } + + uint width, height; if (Service.Config.IsScale || !enabled) { @@ -258,15 +256,61 @@ public sealed unsafe class Plugin : IDalamudPlugin height = 256; } - if (width != dev->Width || height != dev->Height) + _requestedResolutionChange |= (*_dev_RequestResolutionChange) != 0; + + if (!_currentlyFakeResize && (_unloading || enabled || _wasEnabled || _requestedResolutionChange) && + (width != dev->Width || height != dev->Height)) { Service.PluginLog.Info($"Changing graphics resolution from {dev->Width} x {dev->Height} to {width} x {height}"); - *_dev_NewWidth = width; - *_dev_NewHeight = height; - *_dev_RequestResolutionChange = 1; - Service.PluginLog.Info($"Changing game window from {win->WindowWidth} x {win->WindowHeight} to {width} x {height}"); - win->WindowWidth = (int) width; - win->WindowHeight = (int) height; + var mode = Service.DebugConfig.ForceSizeMode; + if (_requestedResolutionChange) + { + // Let's try to be cautious about other plugins (f.e. SimpleTweaks) changing the game resolution. + Service.PluginLog.Info($"Game resolution was changed externally - forcing resize via device, not window resize."); + mode = ForceSizeMode.LegacyRequestResolutionChange; + _requestedResolutionChange = false; + } + switch (mode) + { + case ForceSizeMode.Skip: + break; + case ForceSizeMode.LegacyRequestResolutionChange: + *_dev_NewWidth = width; + *_dev_NewHeight = height; + *_dev_RequestResolutionChange = 1; + Service.PluginLog.Info($"Changing game window from {win->WindowWidth} x {win->WindowHeight} to {width} x {height}"); + win->WindowWidth = (int) width; + win->WindowHeight = (int) height; + break; + case ForceSizeMode.FakeWindowResize: + var adjustedClientRect = new RECT(0, 0, rectWidth, rectHeight); + AdjustWindowRect(&adjustedClientRect, (uint) GetWindowLongPtr(_currentHwnd, GWL.GWL_STYLE), false); + var adjustedWidth = adjustedClientRect.right - adjustedClientRect.left; + var adjustedHeight = adjustedClientRect.bottom - adjustedClientRect.top; + Service.PluginLog.Info($"Resizing window to {adjustedWidth} x {adjustedHeight}"); + try + { + _currentlyFakeResize = true; + SendMessage(_currentHwnd, WM.WM_ENTERSIZEMOVE, 0, 0); + SendMessage(_currentHwnd, WM.WM_SIZE, SIZE_MAXSHOW, WndProcHook.SizeToParam((uint) adjustedWidth, (uint) adjustedHeight)); + SendMessage(_currentHwnd, WM.WM_PAINT, 0, 0); + SendMessage(_currentHwnd, WM.WM_EXITSIZEMOVE, 0, 0); + } + finally + { + _currentlyFakeResize = false; + } + break; + default: + Service.PluginLog.Error($"Unknown ForceSizeMode: {Service.DebugConfig.ForceSizeMode}"); + break; + } + } + + if (_wasEnabled != enabled) + { + Service.PluginLog.Info($"Changed state to: {enabled}"); + _wasEnabled = enabled; } //Service.PluginLog.Debug($"NewWidth 0x{(long) (IntPtr) (&dev->NewWidth):X16}"); @@ -423,7 +467,7 @@ public sealed unsafe class Plugin : IDalamudPlugin { case SystemConfigOption.ScreenWidth: case SystemConfigOption.ScreenHeight: - if (Service.DebugConfig.SetSizeMode == SetSizeMode.InterceptSystemConfig) + if (Service.DebugConfig.SetWindowSizeMode == SetWindowSizeMode.InterceptSystemConfig) { var name = e.ConfigOption.ToString(); var valueOrig = Service.GameConfig.System.GetUInt(name); @@ -448,3 +492,22 @@ public sealed unsafe class Plugin : IDalamudPlugin } } } + +public enum ForceSizeMode +{ + Skip = -1, + + /// + /// Fake a window resize event. + /// + FakeWindowResize = 0, + + /// + /// Legacy mode, went through more initial testing, works well except for conflicts with other plugins. + /// Might revert or remove depending on how FakeWindowResize testing / fixups proceed. + /// + /// Update the graphics device width / height and set RequestResolutionChange. + /// This is the same method that other plugins such as SimpleTweaks or XIVWindowResizer use. + /// + LegacyRequestResolutionChange = 1 +} diff --git a/README.md b/README.md index e2ff836..d61d7a3 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ time than I have on my hands right now.
I'm using DLSS, and my game looks pixelated / is small! -DLSS and cres at very small scales (width / height under about 500 pixels) seems to be a conflicting combo. -Resizing the game window usually undoes any miniaturization, but I've yet to figure out why the game pixelates. Sorry! +DLSS in FFXIV is bugged. No, I'm not joking: even without any plugins or mods loaded, it can glitch out when +simply resizing the window (most notably by rapidly pressing WinKey + Down and WinKey + Up). + +This issue doesn't happen with FSR and non-DLSS dynamic resolution.