DP-CustomResolution/CustomResolution/Hooks/WndProcHook.cs

142 lines
3.3 KiB
C#

using CustomResolution.WndProcHookManagerProxyApi;
using Serilog.Events;
using System;
using System.Linq;
using System.Reflection.Emit;
using TerraFX.Interop.Windows;
namespace CustomResolution.Hooks;
// THIS. IS. UGLY.
public sealed class WndProcHook : IDisposable
{
private const uint WM_MOUSEFIRST = 0x0200;
private const uint WM_MOUSELAST = 0x0209;
private static WndProcHook? _instance;
private readonly WndProcHookManager _manager;
private bool _applied = false;
private DynamicMethod? _genMethod;
private Delegate? _genDelegate;
public WndProcHook()
{
_instance = this;
_manager = new();
Apply();
}
public void Dispose()
{
if (!_applied)
{
return;
}
if (_manager.PreWndProc is not { } preWndProcProp)
{
Service.PluginLog.Information("CustomResolution couldn't obtain the PreWndProc event.");
return;
}
preWndProcProp.RemoveEventHandler(_manager.ProxiedValue, _genDelegate);
_applied = false;
}
public static void InvokeStatic(object args)
{
if (_instance is { } instance)
{
instance.Invoke(new WndProcEventArgs(instance._manager, args));
}
}
private static void ParamToCoords(LPARAM param, out int x, out int y)
{
x = (short) (ushort) (param.Value & 0xFFFF);
y = (short) (ushort) (param.Value >> 16 & 0xFFFF);
}
private static LPARAM CoordsToParam(int x, int y)
{
nint value = 0;
value |= ((ushort) (short) x) & 0xFFFF;
value |= ((ushort) (short) y) >> 16 & 0xFFFF;
return value;
}
private void Invoke(WndProcEventArgs args)
{
if (!(WM_MOUSEFIRST <= args.Message && args.Message <= WM_MOUSELAST))
{
return;
}
ParamToCoords(args.LParam, out int x, out int y);
#if false
Service.PluginLog.Debug($"WM_MOUSE A @ {x} {y}");
#endif
Service.Plugin.ConvertCoordsWinToGame(ref x, ref y);
#if false
Service.PluginLog.Debug($"WM_MOUSE B @ {x} {y}");
#endif
args.LParam = CoordsToParam(x, y);
}
private void Apply()
{
if (!_manager.Refresh())
{
return;
}
if (_applied)
{
Dispose();
}
if (_manager.PreWndProc is not { } preWndProcProp)
{
Service.PluginLog.Information("CustomResolution couldn't obtain the PreWndProc event.");
return;
}
if (_genDelegate is null)
{
var delegateType = preWndProcProp.EventHandlerType!;
var delegateInvoke = delegateType.GetMethod("Invoke")!;
_genMethod = new DynamicMethod(
"CustomResolution_PreWndProc",
delegateInvoke.ReturnType,
delegateInvoke.GetParameters().Select(p => p.ParameterType).ToArray()
);
var il = _genMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(WndProcHook).GetMethod("InvokeStatic")!);
il.Emit(OpCodes.Ret);
_genDelegate = _genMethod.CreateDelegate(delegateType);
}
preWndProcProp.AddEventHandler(_manager.ProxiedValue, _genDelegate);
_applied = true;
}
}