diff --git a/Assets/Common/AddressRecords.json b/Assets/Common/AddressRecords.json
index bf4783a..bd11e23 100644
--- a/Assets/Common/AddressRecords.json
+++ b/Assets/Common/AddressRecords.json
@@ -463,5 +463,10 @@
"Name": "EmAction:VTableAssignment",
"Pattern": "C7 41 0C 89 88 88 3C 89 71 08 48 8D 05 ?? ?? ?? ?? 48 89 01",
"Offset": 13
+ },
+ {
+ "Name": "Keyboard:WriteInput",
+ "Pattern": "41 57 48 81 EC 20 01 00 00 48 8B E9 48 8B FA 48 8B 89 10 0A 00 00",
+ "Offset": -10
}
]
diff --git a/SharpPluginLoader.Core/Rendering/Renderer.cs b/SharpPluginLoader.Core/Rendering/Renderer.cs
index d1af85d..48d608e 100644
--- a/SharpPluginLoader.Core/Rendering/Renderer.cs
+++ b/SharpPluginLoader.Core/Rendering/Renderer.cs
@@ -6,7 +6,6 @@
using ImGuiNET;
using SharpPluginLoader.Core.IO;
using SharpPluginLoader.Core.Memory;
-using SharpPluginLoader.Core.MtTypes;
namespace SharpPluginLoader.Core.Rendering
{
@@ -68,7 +67,7 @@ public static TextureHandle LoadTexture(string path, out uint width, out uint he
/// Fonts must be registered before the first call to .
/// Ideally, fonts should be registered in the method.
///
- public static unsafe void RegisterFont(string name, string path, float size, nint glyphRanges = 0,
+ public static unsafe void RegisterFont(string name, string path, float size, nint glyphRanges = 0,
bool merge = false, int oversampleV = 0, int oversampleH = 0)
{
if (_fontsSubmitted)
@@ -232,10 +231,13 @@ Initializing Renderer with
if (ImGui.GetCurrentContext() != 0)
return ImGui.GetCurrentContext();
-
+
ImGui.CreateContext();
var io = ImGui.GetIO();
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;
+ // Set NavNoCaptureKeyboard because it doesn't include ctrl+tab and handle it by
+ // checking io.NavActive when deciding to block keyboard or not.
+ io.ConfigFlags |= ImGuiConfigFlags.NavNoCaptureKeyboard;
// Currently causes a freeze when dragging a window outside of the main window.
// Most likely the WndProc doesn't process events anymore which causes windows to think it's frozen.
// io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
@@ -247,14 +249,119 @@ Initializing Renderer with
{
_mouseUpdateHook = Hook.Create(sMhMouse.GetVirtualFunction(6), m =>
{
- // Prevent the game from doing any mouse updates if an ImGui window is focused.
- if (ImGui.GetIO().MouseDrawCursor)
- return;
+ var anyFocused = ImGui.IsWindowFocused(ImGuiFocusedFlags.AnyWindow);
+ var anyHovered = ImGui.IsWindowHovered(ImGuiHoveredFlags.AnyWindow);
+
+ if (anyFocused || anyHovered)
+ {
+ // Tell the game to not reset the cursor to the middle of the screen
+ // when Camera Mouse Controls are on.
+ // MonsterHunterWorld.exe+4898F0 - cmp [rax+000147A8],r15b(0)
+ MemoryUtil.GetRef(Gui.SingletonInstance.Instance + 0x147A8) = 0x1;
+ }
_mouseUpdateHook.Original(m);
+
+ if (anyFocused || anyHovered)
+ {
+ MemoryUtil.GetRef(m + 0x17C) = 0; // Scroll wheel.
+ }
+
+ if (anyFocused)
+ {
+ // Zero mouse delta used for camera movement.
+ MemoryUtil.GetRef(m + 0xFC) = 0L; // dX(int), dY(int).
+ MemoryUtil.GetRef(m + 0x188) = 0x0; // Mouse down menu.
+ MemoryUtil.GetRef(m + 0x108) = 0x0; // Mouse down combat.
+ _lastUpdateHadFocus = true;
+ }
+ else if (anyHovered || _lastUpdateHadFocus)
+ {
+ // Block mouse1 clicks. Use _lastUpdateHadFocus to more consistently block
+ // a click used to unfocus the ImGui window.
+ ref byte downMenu = ref MemoryUtil.GetRef(m + 0x188);
+ ref byte downCombat = ref MemoryUtil.GetRef(m + 0x108);
+ if ((downMenu & 0x1) == 0 && (downCombat & 0x1) == 0) // Wait for release.
+ {
+ _lastUpdateHadFocus = false;
+ }
+ downMenu &= 0xFE;
+ downCombat &= 0xFE;
+ }
+ });
+ }
+
+ var sMhKeyboard = SingletonManager.GetSingleton("sMhKeyboard");
+ if (sMhKeyboard is not null)
+ {
+ _keyboardUpdateHook = Hook.Create(AddressRepository.Get("Keyboard:WriteInput"), (kb, kbState) =>
+ {
+ _keyboardUpdateHook.Original(kb, kbState);
+
+ if (_lastUpdateHadKeyboard)
+ {
+ // Block Escape/Enter if they were presumably used to exit an input field.
+ // If for some reason the menu key is being held here, this will supersede it
+ // and cause the menu to be re-toggled on the release of Escape or Enter.
+ if (Input.IsDown(Key.Escape))
+ {
+ _waitForRelease = Key.Escape;
+ }
+ else if (Input.IsDown(Key.Enter))
+ {
+ _waitForRelease = Key.Enter;
+ }
+ }
+
+ if (_waitForRelease == null)
+ {
+ // Block the key used to bring up the menu.
+ if (Input.IsDown(_menuKey))
+ {
+ _showMenu = !_showMenu;
+ _waitForRelease = _menuKey;
+ }
+#if DEBUG
+ else if (Input.IsDown(_demoKey))
+ {
+ _showDemo = !_showDemo;
+ _waitForRelease = _demoKey;
+ }
+#endif
+ }
+
+ unsafe
+ {
+ KeyboardState* state = (KeyboardState*)kbState;
+
+ if (_waitForRelease != null)
+ {
+ byte* vkTable = (byte*)(kb + 0x38);
+ byte vk = vkTable[(int)_waitForRelease];
+ uint vkMask = 1u << (vk & 0x1F);
+ // No longer down = Released.
+ if ((state->On[vk >> 5] & vkMask) == 0)
+ {
+ _waitForRelease = null;
+ }
+ else
+ {
+ // On the update a key is first "Down", it seems to only be set in
+ // KeyboardState::On. So negating that should effectively block it.
+ state->On[vk >> 5] &= ~vkMask;
+ }
+ }
+
+ var io = ImGui.GetIO();
+ _lastUpdateHadKeyboard = io.WantCaptureKeyboard || io.NavActive;
+ if (_lastUpdateHadKeyboard)
+ {
+ Unsafe.InitBlockUnaligned((byte*)state, 0, (uint)Marshal.SizeOf());
+ }
+ }
});
}
-
+
Log.Debug("Renderer.Initialize");
return ImGui.GetCurrentContext();
@@ -270,11 +377,6 @@ internal static void Render()
[UnmanagedCallersOnly]
internal static unsafe nint ImGuiRender()
{
- if (Input.IsPressed(_menuKey))
- _showMenu = !_showMenu;
- if (Input.IsPressed(_demoKey))
- _showDemo = !_showDemo;
-
var io = ImGui.GetIO();
var anyFocused = ImGui.IsWindowFocused(ImGuiFocusedFlags.AnyWindow);
var anyHovered = ImGui.IsWindowHovered(ImGuiHoveredFlags.AnyWindow);
@@ -293,6 +395,19 @@ internal static unsafe nint ImGuiRender()
{
if (ImGui.BeginMenu("Options"))
{
+ bool keyboardNav = (io.ConfigFlags & ImGuiConfigFlags.NavEnableKeyboard) != 0;
+ if (ImGui.Checkbox("Keyboard Navigation", ref keyboardNav))
+ {
+ if (keyboardNav)
+ {
+ io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
+ }
+ else
+ {
+ io.ConfigFlags &= ~ImGuiConfigFlags.NavEnableKeyboard;
+ }
+ }
+
ImGui.Checkbox("Draw Primitives as Wireframe",
ref MemoryUtil.AsRef(_renderingOptionPointers.DrawPrimitivesAsWireframe));
@@ -341,7 +456,7 @@ ref MemoryUtil.AsRef(_renderingOptionPointers.LineThickness),
ImGui.ShowDemoWindow(ref _showDemo);
#endif
- ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 5f);
+ ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 5.0f);
InternalCalls.RenderNotifications();
ImGui.PopStyleVar();
@@ -473,16 +588,23 @@ private static nint GetCursorPositionHook(nint app, out Point pos)
private delegate void MouseUpdateDelegate(nint sMhMouse);
private static Hook _getCursorPositionHook = null!;
private static Hook _mouseUpdateHook = null!;
+ private static bool _lastUpdateHadFocus = false;
+ private delegate void KeyboardUpdateDelegate(nint sMhKeyboard, nint kbState);
+ private static Hook _keyboardUpdateHook = null!;
+ private static bool _lastUpdateHadKeyboard = false;
+ private static Key? _waitForRelease = null;
private static bool _showMenu = false;
+ private static Key _menuKey = DefaultMenuKey;
+#if DEBUG
private static bool _showDemo = false;
+ private static Key _demoKey = DefaultDemoKey;
+#endif
private static RenderingOptionPointers _renderingOptionPointers;
private static Vector2 _viewportSize;
private static Vector2 _windowSize;
private static Vector2 _mousePos;
private static Vector2 _mousePosScalingFactor;
private static float _fontScale = 1.0f;
- private static Key _menuKey = DefaultMenuKey;
- private static Key _demoKey = DefaultDemoKey;
private static bool _fontsSubmitted = false;
private static NativeArray CustomFonts;