Compare commits

...

4 commits

12 changed files with 253 additions and 67 deletions

View file

@ -1,6 +1,4 @@
using static FFXIVClientStructs.FFXIV.Client.UI.AddonRelicNoteBook; using System.Globalization;
using System.Collections;
using System.Globalization;
using System; using System;
namespace CustomResolution.Cmds; namespace CustomResolution.Cmds;
@ -66,18 +64,28 @@ public sealed class MainCmd : Cmd
return; return;
case "debugon": case "debugon":
Service.Plugin.IsDebug = true; Service.DebugConfig.IsDebug = true;
Service.PrintChat("Enabled cres debug."); Service.PrintChat("Enabled cres debug.");
return; return;
case "debugoff": case "debugoff":
Service.Plugin.IsDebug = false; Service.DebugConfig.IsDebug = false;
Service.PrintChat("Disabled cres debug."); Service.PrintChat("Disabled cres debug.");
return; return;
case "debug": case "debug":
Service.Plugin.IsDebug = !Service.Plugin.IsDebug; Service.DebugConfig.IsDebug = !Service.DebugConfig.IsDebug;
Service.PrintChat($"{(Service.Plugin.IsDebug ? "Enabled" : "Disabled")} cres debug."); Service.PrintChat($"{(Service.DebugConfig.IsDebug ? "Enabled" : "Disabled")} cres debug.");
return;
case "debugsizeold":
Service.DebugConfig.SetSizeMode = SetSizeMode.LegacyHookSetWindowPos;
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.PrintChat("Switched back to new size mode.");
return; return;
} }

View file

@ -23,6 +23,8 @@ public class Configuration : IPluginConfiguration
public DXVKDWMHackMode DXVKDWMHackMode = DXVKDWMHackMode.Off; public DXVKDWMHackMode DXVKDWMHackMode = DXVKDWMHackMode.Off;
public MinSizeMode MinSizeMode = MinSizeMode.Unchanged;
[NonSerialized] [NonSerialized]
private IDalamudPluginInterface? pluginInterface; private IDalamudPluginInterface? pluginInterface;
@ -123,3 +125,26 @@ public static class ModifierKeyExt
_ => null _ => null
}; };
} }
public enum MinSizeMode
{
Unchanged = 0,
Unlocked = 1
}
public static class MinSizeModeExt
{
public static string ToHumanNameString(this MinSizeMode mode) => mode switch
{
MinSizeMode.Unchanged => "Unchanged (1024x768)",
MinSizeMode.Unlocked => "Unlocked",
_ => mode.ToString(),
};
public static string? ToHumanInfoString(this MinSizeMode mode) => mode switch
{
MinSizeMode.Unchanged => "The game normally doesn't allow resizing to smaller than 1024x768.",
MinSizeMode.Unlocked => "Allow resizing the game in windowed mode to smaller than 1024x768. Works best with scaling over 100%.",
_ => null
};
}

View file

@ -18,6 +18,7 @@
<ProduceReferenceAssembly>false</ProduceReferenceAssembly> <ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<RootNamespace>CustomResolution</RootNamespace>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -0,0 +1,29 @@
using System;
namespace CustomResolution;
/// <summary>
/// Flags for internal testing.
/// </summary>
public class DebugConfiguration
{
public bool IsDebug { get; set; } = false;
public SetSizeMode SetSizeMode { get; set; } = SetSizeMode.InterceptSystemConfig;
}
public enum SetSizeMode
{
/// <summary>
/// Intercept the system config width / height and scale it, but 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
/// </summary>
InterceptSystemConfig = 0,
/// <summary>
/// Legacy mode, went through more testing, works well except for the game getting confused about windowed mode size.
/// Might revert or remove depending on how InterceptSystemConfig testing / fixups proceed.
/// </summary>
LegacyHookSetWindowPos = 1
}

View file

@ -1,13 +1,8 @@
using CustomResolution.WndProcHookManagerProxyApi; using Dalamud.Hooking;
using Dalamud.Hooking;
using Serilog.Events;
using System; using System;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
namespace CustomResolution.Hooks; namespace CustomResolution.Hooks;
// THIS. IS. UGLY. // THIS. IS. UGLY.

View file

@ -1,13 +1,8 @@
using CustomResolution.WndProcHookManagerProxyApi; using Dalamud.Hooking;
using Dalamud.Hooking;
using Serilog.Events;
using System; using System;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
namespace CustomResolution.Hooks; namespace CustomResolution.Hooks;
// THIS. IS. UGLY. // THIS. IS. UGLY.
@ -34,6 +29,7 @@ public sealed unsafe class WindowRectHooks : IDisposable
public void Dispose() public void Dispose()
{ {
_getClientRectHook.Dispose(); _getClientRectHook.Dispose();
_setWindowPosHook.Dispose();
} }
public bool GetClientRectOrig(HWND hWnd, RECT* lpRect) public bool GetClientRectOrig(HWND hWnd, RECT* lpRect)
@ -69,7 +65,10 @@ public sealed unsafe class WindowRectHooks : IDisposable
Service.PluginLog.Debug($"SetWindowPosDetour A @ {X} {Y} {cx} {cy}"); Service.PluginLog.Debug($"SetWindowPosDetour A @ {X} {Y} {cx} {cy}");
#endif #endif
Service.Plugin.ConvertCoordsGameToWin(ref cx, ref cy); if (Service.DebugConfig.SetSizeMode == SetSizeMode.LegacyHookSetWindowPos)
{
Service.Plugin.ConvertCoordsGameToWin(ref cx, ref cy);
}
#if false #if false
Service.PluginLog.Debug($"SetWindowPosDetour B @ {X} {Y} {cx} {cy}"); Service.PluginLog.Debug($"SetWindowPosDetour B @ {X} {Y} {cx} {cy}");

View file

@ -1,12 +1,11 @@
using CustomResolution.WndProcHookManagerProxyApi; using CustomResolution.WndProcHookManagerProxyApi;
using Serilog.Events;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection.Emit; using System.Reflection.Emit;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
using static TerraFX.Interop.Windows.Windows; using static TerraFX.Interop.Windows.Windows;
namespace CustomResolution.Hooks; namespace CustomResolution.Hooks;
// THIS. IS. UGLY. // THIS. IS. UGLY.
@ -17,8 +16,10 @@ public sealed unsafe class WndProcHook : IDisposable
private readonly WndProcHookManager _manager; private readonly WndProcHookManager _manager;
private bool _applied = false; private bool _applied = false;
private DynamicMethod? _genMethod; private DynamicMethod? _genMethodPre;
private Delegate? _genDelegate; private Delegate? _genDelegatePre;
private DynamicMethod? _genMethodPost;
private Delegate? _genDelegatePost;
public WndProcHook() public WndProcHook()
{ {
@ -36,23 +37,40 @@ public sealed unsafe class WndProcHook : IDisposable
return; return;
} }
if (_manager.PreWndProc is not { } preWndProcProp) if (_manager.PreWndProc is { } preWndProcProp)
{
preWndProcProp.RemoveEventHandler(_manager.ProxiedValue, _genDelegatePre);
}
else
{ {
Service.PluginLog.Information("CustomResolution couldn't obtain the PreWndProc event."); Service.PluginLog.Information("CustomResolution couldn't obtain the PreWndProc event.");
return;
} }
preWndProcProp.RemoveEventHandler(_manager.ProxiedValue, _genDelegate); if (_manager.PostWndProc is { } postWndProcProp)
{
postWndProcProp.RemoveEventHandler(_manager.ProxiedValue, _genDelegatePost);
}
else
{
Service.PluginLog.Information("CustomResolution couldn't obtain the PostWndProc event.");
}
_applied = false; _applied = false;
} }
public static void InvokeStatic(object args) public static void InvokeStaticPre(object args)
{ {
if (_instance is { } instance) if (_instance is { } instance)
{ {
instance.Invoke(new WndProcEventArgs(instance._manager, args)); instance.InvokePre(new WndProcEventArgs(instance._manager, args));
}
}
public static void InvokeStaticPost(object args)
{
if (_instance is { } instance)
{
instance.InvokePost(new WndProcEventArgs(instance._manager, args));
} }
} }
@ -86,13 +104,19 @@ public sealed unsafe class WndProcHook : IDisposable
return value; return value;
} }
private void Invoke(WndProcEventArgs args) private void InvokePre(WndProcEventArgs args)
{ {
if (Service.Plugin is not { } plugin) if (Service.Plugin is not { } plugin)
{ {
return; return;
} }
if (args.Message == WM.WM_WINDOWPOSCHANGING &&
Service.Config.MinSizeMode == MinSizeMode.Unlocked)
{
args.SuppressCall = true;
}
if (args.Message == WM.WM_SIZE) if (args.Message == WM.WM_SIZE)
{ {
ParamToCoords(args.LParam, out int x, out int y); ParamToCoords(args.LParam, out int x, out int y);
@ -153,6 +177,14 @@ public sealed unsafe class WndProcHook : IDisposable
} }
} }
private void InvokePost(WndProcEventArgs args)
{
if (Service.Plugin is not { } plugin)
{
return;
}
}
private void Apply() private void Apply()
{ {
if (!_manager.Refresh()) if (!_manager.Refresh())
@ -165,34 +197,56 @@ public sealed unsafe class WndProcHook : IDisposable
Dispose(); Dispose();
} }
if (_manager.PreWndProc is not { } preWndProcProp) if (_manager.PreWndProc is not { } preWndProcProp ||
_manager.PostWndProc is not { } postWndProcProp)
{ {
Service.PluginLog.Information("CustomResolution couldn't obtain the PreWndProc event."); Service.PluginLog.Information("CustomResolution couldn't obtain the PreWndProc / PostWndProc events.");
return; return;
} }
if (_genDelegate is null) if (_genDelegatePre is null)
{ {
var delegateType = preWndProcProp.EventHandlerType!; var delegateType = preWndProcProp.EventHandlerType!;
var delegateInvoke = delegateType.GetMethod("Invoke")!; var delegateInvoke = delegateType.GetMethod("Invoke")!;
_genMethod = new DynamicMethod( _genMethodPre = new DynamicMethod(
"CustomResolution_PreWndProc", "CustomResolution_PreWndProc",
delegateInvoke.ReturnType, delegateInvoke.ReturnType,
delegateInvoke.GetParameters().Select(p => p.ParameterType).ToArray() delegateInvoke.GetParameters().Select(p => p.ParameterType).ToArray()
); );
var il = _genMethod.GetILGenerator(); var il = _genMethodPre.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(WndProcHook).GetMethod("InvokeStatic")!); il.Emit(OpCodes.Call, typeof(WndProcHook).GetMethod(nameof(InvokeStaticPre))!);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
_genDelegate = _genMethod.CreateDelegate(delegateType); _genDelegatePre = _genMethodPre.CreateDelegate(delegateType);
} }
preWndProcProp.AddEventHandler(_manager.ProxiedValue, _genDelegate); if (_genDelegatePost is null)
{
var delegateType = preWndProcProp.EventHandlerType!;
var delegateInvoke = delegateType.GetMethod("Invoke")!;
_genMethodPost = new DynamicMethod(
"CustomResolution_PostWndProc",
delegateInvoke.ReturnType,
delegateInvoke.GetParameters().Select(p => p.ParameterType).ToArray()
);
var il = _genMethodPost.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(WndProcHook).GetMethod(nameof(InvokeStaticPost))!);
il.Emit(OpCodes.Ret);
_genDelegatePost = _genMethodPost.CreateDelegate(delegateType);
}
preWndProcProp.AddEventHandler(_manager.ProxiedValue, _genDelegatePre);
postWndProcProp.AddEventHandler(_manager.ProxiedValue, _genDelegatePost);
_applied = true; _applied = true;
} }

View file

@ -1,19 +1,15 @@
using CustomResolution.Hooks; using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.ClientState.Keys; using Dalamud.Game.Config;
using Dalamud.IoC;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Common.Lua;
using FFXIVClientStructs.Interop; using FFXIVClientStructs.Interop;
using ImGuiNET; using ImGuiNET;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
using static Dalamud.Interface.Utility.Raii.ImRaii;
using static TerraFX.Interop.Windows.Windows; using static TerraFX.Interop.Windows.Windows;
namespace CustomResolution; namespace CustomResolution;
@ -30,6 +26,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
private HWND _currentHwnd; private HWND _currentHwnd;
private RECT _currentClientRect; private RECT _currentClientRect;
private DXVKDWMHackMode _currentDXVKDWMHackMode = DXVKDWMHackMode.Off; private DXVKDWMHackMode _currentDXVKDWMHackMode = DXVKDWMHackMode.Off;
private bool _ignoreConfigChanges = false;
public Plugin(IDalamudPluginInterface pluginInterface) public Plugin(IDalamudPluginInterface pluginInterface)
{ {
@ -39,6 +36,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
Service.Plugin = this; Service.Plugin = this;
Service.DebugConfig = new DebugConfiguration();
Service.Config = Service.PluginInterface.GetPluginConfig() as Configuration ?? new(); Service.Config = Service.PluginInterface.GetPluginConfig() as Configuration ?? new();
Service.Config.Initialize(Service.PluginInterface); Service.Config.Initialize(Service.PluginInterface);
@ -59,6 +58,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
} }
Service.Framework.Update += OnFrameworkUpdate; Service.Framework.Update += OnFrameworkUpdate;
Service.GameConfig.SystemChanged += OnSystemConfigChanged;
} }
public string Name => "CustomResolution"; public string Name => "CustomResolution";
@ -72,8 +72,6 @@ public sealed unsafe class Plugin : IDalamudPlugin
public bool CurrentBorderlessFullscreen { get; private set; } public bool CurrentBorderlessFullscreen { get; private set; }
public bool IsDebug { get; set; }
public void Dispose() public void Dispose()
{ {
_unloading = true; _unloading = true;
@ -81,6 +79,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
lock (_disposeLock) lock (_disposeLock)
{ {
Service.Framework.Update -= OnFrameworkUpdate; Service.Framework.Update -= OnFrameworkUpdate;
Service.GameConfig.SystemChanged -= OnSystemConfigChanged;
Service.Framework.RunOnFrameworkThread(Update); Service.Framework.RunOnFrameworkThread(Update);
} }
@ -106,8 +105,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
return; return;
} }
float scaleX = CurrentWidth / (float) CurrentWindowWidth; var scaleX = CurrentWidth / (float) CurrentWindowWidth;
float scaleY = CurrentHeight / (float) CurrentWindowHeight; var scaleY = CurrentHeight / (float) CurrentWindowHeight;
x = (int) Math.Round(x * scaleX); x = (int) Math.Round(x * scaleX);
y = (int) Math.Round(y * scaleY); y = (int) Math.Round(y * scaleY);
@ -120,8 +119,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
return; return;
} }
float scaleX = CurrentWindowWidth / (float) CurrentWidth; var scaleX = CurrentWindowWidth / (float) CurrentWidth;
float scaleY = CurrentWindowHeight / (float) CurrentHeight; var scaleY = CurrentWindowHeight / (float) CurrentHeight;
x = (int) Math.Round(x * scaleX); x = (int) Math.Round(x * scaleX);
y = (int) Math.Round(y * scaleY); y = (int) Math.Round(y * scaleY);
@ -134,8 +133,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
return; return;
} }
float scaleX = CurrentWidth / (float) CurrentWindowWidth; var scaleX = CurrentWidth / (float) CurrentWindowWidth;
float scaleY = CurrentHeight / (float) CurrentWindowHeight; var scaleY = CurrentHeight / (float) CurrentWindowHeight;
var p = new POINT(x, y); var p = new POINT(x, y);
@ -157,8 +156,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
return; return;
} }
float scaleX = CurrentWindowWidth / (float) CurrentWidth; var scaleX = CurrentWindowWidth / (float) CurrentWidth;
float scaleY = CurrentWindowHeight / (float) CurrentHeight; var scaleY = CurrentWindowHeight / (float) CurrentHeight;
var p = new POINT(x, y); var p = new POINT(x, y);
@ -186,8 +185,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
Service.WindowRectHooks.GetClientRectOrig(_currentHwnd, currentClientRectPtr); Service.WindowRectHooks.GetClientRectOrig(_currentHwnd, currentClientRectPtr);
} }
int rectWidth = _currentClientRect.right - _currentClientRect.left; var rectWidth = _currentClientRect.right - _currentClientRect.left;
int rectHeight = _currentClientRect.bottom - _currentClientRect.top; var rectHeight = _currentClientRect.bottom - _currentClientRect.top;
if ((rectWidth <= 0 || rectHeight <= 0) && !_unloading) if ((rectWidth <= 0 || rectHeight <= 0) && !_unloading)
{ {
@ -196,7 +195,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
uint width, height; uint width, height;
bool enabled = !_unloading && Service.Config.IsEnabled; var enabled = !_unloading && Service.Config.IsEnabled;
if (_wasEnabled != enabled) if (_wasEnabled != enabled)
{ {
Service.PluginLog.Info($"Changing state to: {enabled}"); Service.PluginLog.Info($"Changing state to: {enabled}");
@ -266,15 +265,15 @@ public sealed unsafe class Plugin : IDalamudPlugin
* Default windowed style / exstyle is 0x14CF0000 / 0. * Default windowed style / exstyle is 0x14CF0000 / 0.
* WS.WS_VISIBLE | WS.WS_CLIPSIBLINGS | WS.WS_CAPTION | WS.WS_SYSMENU | WS.WS_THICKFRAME | WS.WS_MINIMIZEBOX | WS.WS_MAXIMIZEBOX * WS.WS_VISIBLE | WS.WS_CLIPSIBLINGS | WS.WS_CAPTION | WS.WS_SYSMENU | WS.WS_THICKFRAME | WS.WS_MINIMIZEBOX | WS.WS_MAXIMIZEBOX
*/ */
uint styleOrig = (uint) GetWindowLong(_currentHwnd, GWL.GWL_STYLE); var styleOrig = (uint) GetWindowLong(_currentHwnd, GWL.GWL_STYLE);
uint exstyleOrig = (uint) GetWindowLong(_currentHwnd, GWL.GWL_EXSTYLE); var exstyleOrig = (uint) GetWindowLong(_currentHwnd, GWL.GWL_EXSTYLE);
uint style = styleOrig; var style = styleOrig;
uint exstyle = exstyleOrig; var exstyle = exstyleOrig;
bool fullscreen = (style & WS.WS_SYSMENU) == 0; var fullscreen = (style & WS.WS_SYSMENU) == 0;
if (IsDebug) if (Service.DebugConfig.IsDebug)
{ {
Service.PluginLog.Info("--------"); Service.PluginLog.Info("--------");
Service.PluginLog.Info($"STYLE: 0x{style:X8}"); Service.PluginLog.Info($"STYLE: 0x{style:X8}");
@ -312,7 +311,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
exstyle &= ~(uint) WS.WS_EX_COMPOSITED; exstyle &= ~(uint) WS.WS_EX_COMPOSITED;
} }
if (IsDebug) if (Service.DebugConfig.IsDebug)
{ {
Service.PluginLog.Info($"NEWSTYLE: 0x{style:X8}"); Service.PluginLog.Info($"NEWSTYLE: 0x{style:X8}");
Service.PluginLog.Info($"NEWEXSTYLE: 0x{exstyle:X8}"); Service.PluginLog.Info($"NEWEXSTYLE: 0x{exstyle:X8}");
@ -320,7 +319,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
if (style != styleOrig || exstyle != exstyleOrig || _currentDXVKDWMHackMode != mode) if (style != styleOrig || exstyle != exstyleOrig || _currentDXVKDWMHackMode != mode)
{ {
if (IsDebug) if (Service.DebugConfig.IsDebug)
{ {
Service.PluginLog.Info("UPDATE"); Service.PluginLog.Info("UPDATE");
} }
@ -331,7 +330,7 @@ public sealed unsafe class Plugin : IDalamudPlugin
SetWindowPos(_currentHwnd, HWND.NULL, 0, 0, 0, 0, SWP.SWP_NOZORDER | SWP.SWP_NOMOVE | SWP.SWP_NOSIZE | SWP.SWP_NOACTIVATE | SWP.SWP_DRAWFRAME); SetWindowPos(_currentHwnd, HWND.NULL, 0, 0, 0, 0, SWP.SWP_NOZORDER | SWP.SWP_NOMOVE | SWP.SWP_NOSIZE | SWP.SWP_NOACTIVATE | SWP.SWP_DRAWFRAME);
ShowWindow(_currentHwnd, SW.SW_SHOW); ShowWindow(_currentHwnd, SW.SW_SHOW);
} }
else if (IsDebug) else if (Service.DebugConfig.IsDebug)
{ {
Service.PluginLog.Info("SAME"); Service.PluginLog.Info("SAME");
} }
@ -363,4 +362,45 @@ public sealed unsafe class Plugin : IDalamudPlugin
Update(); Update();
} }
} }
private void OnSystemConfigChanged(object? sender, ConfigChangeEvent raw)
{
if (_ignoreConfigChanges)
{
return;
}
if (raw is not ConfigChangeEvent<SystemConfigOption> { } e)
{
return;
}
switch (e.ConfigOption)
{
case SystemConfigOption.ScreenWidth:
case SystemConfigOption.ScreenHeight:
if (Service.DebugConfig.SetSizeMode == SetSizeMode.InterceptSystemConfig)
{
var name = e.ConfigOption.ToString();
var valueOrig = Service.GameConfig.System.GetUInt(name);
var isW = e.ConfigOption == SystemConfigOption.ScreenWidth;
var scale = (isW ? CurrentWindowWidth : CurrentWindowHeight) / (float) (isW ? CurrentWidth : CurrentHeight);
var valueNew = (uint) Math.Round(valueOrig * scale);
Service.PluginLog.Info($"Intercepting config value change for {name}: {valueOrig} -> {valueNew}");
try
{
_ignoreConfigChanges = true;
Service.GameConfig.System.Set(name, valueNew);
}
finally
{
_ignoreConfigChanges = false;
}
}
break;
}
}
} }

View file

@ -12,6 +12,8 @@ public sealed class Service
public static Configuration Config { get; internal set; } = null!; public static Configuration Config { get; internal set; } = null!;
public static DebugConfiguration DebugConfig { get; internal set; } = null!;
public static PluginUI PluginUI { get; internal set; } = null!; public static PluginUI PluginUI { get; internal set; } = null!;
public static WndProcHook WndProcHook { get; internal set; } = null!; public static WndProcHook WndProcHook { get; internal set; } = null!;
@ -42,6 +44,9 @@ public sealed class Service
[PluginService] [PluginService]
public static IKeyState KeyState { get; private set; } = null!; public static IKeyState KeyState { get; private set; } = null!;
[PluginService]
public static IGameConfig GameConfig { get; private set; } = null!;
public static void PrintChat(string msg) public static void PrintChat(string msg)
{ {
ChatGui.Print(new XivChatEntry ChatGui.Print(new XivChatEntry

View file

@ -21,6 +21,7 @@ public class ConfigWindow : Window, IDisposable
private float _configScale; private float _configScale;
private int[] _configWH = new int[2]; private int[] _configWH = new int[2];
private DXVKDWMHackMode _configDXVKDWMHackMode; private DXVKDWMHackMode _configDXVKDWMHackMode;
private MinSizeMode _configMinSizeMode;
public ConfigWindow() : base( public ConfigWindow() : base(
"CustomResolution", "CustomResolution",
@ -45,6 +46,7 @@ public class ConfigWindow : Window, IDisposable
_configWH[0] = (int) config.Width; _configWH[0] = (int) config.Width;
_configWH[1] = (int) config.Height; _configWH[1] = (int) config.Height;
_configDXVKDWMHackMode = config.DXVKDWMHackMode; _configDXVKDWMHackMode = config.DXVKDWMHackMode;
_configMinSizeMode = config.MinSizeMode;
} }
public void UpdateToConfig() public void UpdateToConfig()
@ -59,6 +61,7 @@ public class ConfigWindow : Window, IDisposable
config.Width = (uint) _configWH[0]; config.Width = (uint) _configWH[0];
config.Height = (uint) _configWH[1]; config.Height = (uint) _configWH[1];
config.DXVKDWMHackMode = _configDXVKDWMHackMode; config.DXVKDWMHackMode = _configDXVKDWMHackMode;
config.MinSizeMode = _configMinSizeMode;
config.Save(); config.Save();
} }
@ -182,6 +185,24 @@ Works even with the scaling above disabled.
Not intended to be used with proper fullscreen."); Not intended to be used with proper fullscreen.");
} }
if (ImGui.BeginCombo("Minimum window size", _configMinSizeMode.ToHumanNameString()))
{
foreach (var mode in Enum.GetValues<MinSizeMode>())
{
if (ImGui.Selectable(mode.ToHumanNameString(), _configMinSizeMode == mode, ImGuiSelectableFlags.None))
{
_configMinSizeMode = mode;
}
if (ImGui.IsItemHovered() && mode.ToHumanInfoString() is { } info)
{
ImGui.SetTooltip(info);
}
}
ImGui.EndCombo();
}
if (ImGui.Button("Save and apply")) if (ImGui.Button("Save and apply"))
{ {
save = true; save = true;

View file

@ -32,4 +32,10 @@ public readonly record struct WndProcEventArgs(WndProcHookManager WndProcHookMan
get => (bool) WndProcHookManager.GetValue(ProxiedValue, nameof(SuppressCall))!; get => (bool) WndProcHookManager.GetValue(ProxiedValue, nameof(SuppressCall))!;
set => WndProcHookManager.SetValue(ProxiedValue, nameof(SuppressCall), value); set => WndProcHookManager.SetValue(ProxiedValue, nameof(SuppressCall), value);
} }
public LRESULT ReturnValue
{
get => (LRESULT) WndProcHookManager.GetValue(ProxiedValue, nameof(ReturnValue))!;
set => WndProcHookManager.SetValue(ProxiedValue, nameof(ReturnValue), value);
}
} }

View file

@ -28,6 +28,7 @@ public class WndProcHookManager
public object? ProxiedValue => _realManager; public object? ProxiedValue => _realManager;
public EventInfo? PreWndProc { get; private set; } public EventInfo? PreWndProc { get; private set; }
public EventInfo? PostWndProc { get; private set; }
public bool Refresh() public bool Refresh()
{ {
@ -95,6 +96,7 @@ public class WndProcHookManager
} }
PreWndProc = _realManagerType.GetEvent("PreWndProc"); PreWndProc = _realManagerType.GetEvent("PreWndProc");
PostWndProc = _realManagerType.GetEvent("PostWndProc");
return true; return true;
} }
@ -276,6 +278,7 @@ public class WndProcHookManager
_realManagerType = default; _realManagerType = default;
PreWndProc = default; PreWndProc = default;
PostWndProc = default;
ClearCache(); ClearCache();
} }