From 0a4fc113f62243f052aade0659f9ea069da02258 Mon Sep 17 00:00:00 2001 From: Zaafar Ahmed Date: Sun, 4 Aug 2019 13:25:46 -0400 Subject: [PATCH] Added Support for queueing the Keyboard/Mouse events before sending them to the ImGui This featured can be turned on/off per event basis. Currently, it is turned off for the Key Up/Down and turned on for Key press, mouse Up/Down/Move --- .../ClickableTransparentOverlay.csproj | 3 +- ClickableTransparentOverlay/HookController.cs | 348 +++++++++++++----- ClickableTransparentOverlay/Overlay.cs | 1 + 3 files changed, 258 insertions(+), 94 deletions(-) diff --git a/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj b/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj index 7db8ec1..1f9405e 100644 --- a/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj +++ b/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj @@ -86,6 +86,7 @@ ..\packages\SixLabors.ImageSharp.1.0.0-beta0006\lib\net472\SixLabors.ImageSharp.dll + ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll @@ -149,4 +150,4 @@ - + \ No newline at end of file diff --git a/ClickableTransparentOverlay/HookController.cs b/ClickableTransparentOverlay/HookController.cs index 60c7a00..e83b3f1 100644 --- a/ClickableTransparentOverlay/HookController.cs +++ b/ClickableTransparentOverlay/HookController.cs @@ -4,17 +4,24 @@ namespace ClickableTransparentOverlay { + using System; + using System.Collections.Generic; using System.Numerics; using System.Windows.Forms; using Gma.System.MouseKeyHook; using ImGuiNET; /// - /// This class Hooks the Global Window Mouse/Keyboard events - /// and pass them into ImGui Overlay. + /// This class Hooks the Global Window Keyboard/Mouse events + /// and pass them into the ImGui Overlay. + /// + /// NOTE: This class might miss/skip a Keyboard/Mouse event + /// if ImGui render function takes a lot of time. Report on GitHub + /// if that happens. /// public class HookController { + private readonly Stack messages; private IKeyboardMouseEvents myHook; private bool enable; private int windowX; @@ -31,12 +38,23 @@ namespace ClickableTransparentOverlay /// public HookController(int x, int y) { + this.messages = new Stack(); this.windowX = x; this.windowY = y; this.enable = true; this.myHook = Hook.GlobalEvents(); } + private enum HookControllerMessageType + { + MouseUpDown, + MouseMove, + MouseWheel, + KeyUp, + KeyDown, + KeyPress, + } + /// /// Enable this class functionality ( only call it once ). /// @@ -101,144 +119,215 @@ namespace ClickableTransparentOverlay this.myHook.Dispose(); } - private void MouseButtonFunction(MouseEventExtArgs e, bool isDownEvent) + /// + /// Tells the HookController if the ImGui is ready to accept keyboard/mouse messages. + /// + public void PopMessages() { - ImGuiIOPtr io = ImGui.GetIO(); - switch (e.Button) + int counter = 0; + int maxCounter = 10; + while (counter < maxCounter && this.messages.Count > 0) { - case MouseButtons.Left: - io.MouseDown[0] = isDownEvent; - break; - case MouseButtons.Right: - io.MouseDown[1] = isDownEvent; - break; - case MouseButtons.Middle: - io.MouseDown[2] = isDownEvent; - break; - case MouseButtons.XButton1: - io.MouseDown[3] = isDownEvent; - break; - case MouseButtons.XButton2: - io.MouseDown[4] = isDownEvent; - break; - case MouseButtons.None: - // TODO: Find out what does this None mean - break; - default: - // TODO: Make a Logger for the whole Overlay - break; - } + var message = this.messages.Pop(); + switch (message.Type) + { + case HookControllerMessageType.MouseUpDown: + this.ProcessMouseUpDown((MouseEventExtArgs)message.E, message.MiscArg, true); + break; + case HookControllerMessageType.MouseMove: + this.ProcessMouseMove((MouseEventArgs)message.E, true); + break; + case HookControllerMessageType.MouseWheel: + this.ProcessMouseWheel((MouseEventExtArgs)message.E, true); + break; + case HookControllerMessageType.KeyUp: + this.ProcessKeyUp((KeyEventArgs)message.E, true); + break; + case HookControllerMessageType.KeyDown: + this.ProcessKeyDown((KeyEventArgs)message.E, true); + break; + case HookControllerMessageType.KeyPress: + this.ProcessKeyPress((KeyPressEventArgs)message.E, true); + break; + default: + break; + } - if (io.WantCaptureMouse) - { - e.Handled = true; + counter++; } } - private void HookMouseUpExt(object sender, MouseEventExtArgs e) + private void PushMessage(HookControllerMessageType type, EventArgs e, bool miscArg = false) { - if (this.enable) + var message = new HookControllerMessage() { - this.MouseButtonFunction(e, false); - } + Type = type, + E = e, + MiscArg = miscArg, + }; + + this.messages.Push(message); } - private void HookMouseDownExt(object sender, MouseEventExtArgs e) + private void ProcessMouseUpDown(MouseEventExtArgs e, bool isDownEvent, bool shouldSendToImGui) { - if (this.enable) + ImGuiIOPtr io = ImGui.GetIO(); + if (shouldSendToImGui) { - this.MouseButtonFunction(e, true); + switch (e.Button) + { + case MouseButtons.Left: + io.MouseDown[0] = isDownEvent; + break; + case MouseButtons.Right: + io.MouseDown[1] = isDownEvent; + break; + case MouseButtons.Middle: + io.MouseDown[2] = isDownEvent; + break; + case MouseButtons.XButton1: + io.MouseDown[3] = isDownEvent; + break; + case MouseButtons.XButton2: + io.MouseDown[4] = isDownEvent; + break; + case MouseButtons.None: + // TODO: Find out what does this None mean + break; + default: + // TODO: Make a Logger for the whole Overlay + break; + } } - } - - private void HookMouseMove(object sender, MouseEventArgs e) - { - if (!this.enable) + else { - return; + this.PushMessage(HookControllerMessageType.MouseUpDown, e, isDownEvent); } - ImGuiIOPtr io = ImGui.GetIO(); - io.MousePos = new Vector2(e.X - this.windowX, e.Y - this.windowY); - - // TODO: Show ImGUI Cursor/Hide ImGui Cursor - // ImGui.GetIO().MouseDrawCursor = true; - // Window32 API ShowCursor(false) + if (io.WantCaptureMouse) + { + e.Handled = true; + } } - private void HookMouseWheelExt(object sender, MouseEventExtArgs e) + private void ProcessMouseMove(MouseEventArgs e, bool shouldSendToImGui) { - if (!this.enable) + if (shouldSendToImGui) { - return; + ImGuiIOPtr io = ImGui.GetIO(); + io.MousePos = new Vector2(e.X - this.windowX, e.Y - this.windowY); + + // TODO: Show ImGUI Cursor/Hide ImGui Cursor + // ImGui.GetIO().MouseDrawCursor = true; + // Window32 API ShowCursor(false) + } + else + { + this.PushMessage(HookControllerMessageType.MouseMove, e); } + } + private void ProcessMouseWheel(MouseEventExtArgs e, bool shouldSendToImGui) + { ImGuiIOPtr io = ImGui.GetIO(); if (io.WantCaptureMouse) { - io.MouseWheel = e.Delta / SystemInformation.MouseWheelScrollDelta; + if (shouldSendToImGui) + { + io.MouseWheel = e.Delta / SystemInformation.MouseWheelScrollDelta; + } + else + { + this.PushMessage(HookControllerMessageType.MouseWheel, e); + } + e.Handled = true; } } - private void HookKeyUp(object sender, KeyEventArgs e) + private void ProcessKeyUp(KeyEventArgs e, bool shouldSendToImGui) { - var io = ImGui.GetIO(); - io.KeysDown[e.KeyValue] = false; + if (shouldSendToImGui) + { + var io = ImGui.GetIO(); + io.KeysDown[e.KeyValue] = false; - switch (e.KeyCode) + switch (e.KeyCode) + { + case Keys.LWin: + case Keys.RWin: + io.KeySuper = false; + break; + case Keys.LControlKey: + case Keys.RControlKey: + io.KeyCtrl = false; + break; + case Keys.LMenu: + case Keys.RMenu: + io.KeyAlt = false; + break; + case Keys.LShiftKey: + case Keys.RShiftKey: + io.KeyShift = false; + break; + default: + break; + } + } + else { - case Keys.LWin: - case Keys.RWin: - io.KeySuper = false; - break; - case Keys.LControlKey: - case Keys.RControlKey: - io.KeyCtrl = false; - break; - case Keys.LMenu: - case Keys.RMenu: - io.KeyAlt = false; - break; - case Keys.LShiftKey: - case Keys.RShiftKey: - io.KeyShift = false; - break; - default: - break; + this.PushMessage(HookControllerMessageType.KeyUp, e); } } - private void HookKeyDown(object sender, KeyEventArgs e) + private void ProcessKeyDown(KeyEventArgs e, bool shouldSendToImGui) { - if (!this.enable) - { - return; - } - var io = ImGui.GetIO(); if (io.WantCaptureKeyboard) { - io.KeysDown[e.KeyValue] = true; + if (shouldSendToImGui) + { + io.KeysDown[e.KeyValue] = true; + } + else + { + this.PushMessage(HookControllerMessageType.KeyDown, e); + } switch (e.KeyCode) { case Keys.LWin: case Keys.RWin: - io.KeySuper = true; + if (shouldSendToImGui) + { + io.KeySuper = true; + } + break; case Keys.LControlKey: case Keys.RControlKey: - io.KeyCtrl = true; + if (shouldSendToImGui) + { + io.KeyCtrl = true; + } + e.Handled = true; break; case Keys.LMenu: // LAlt is LMenu case Keys.RMenu: // RAlt is RMenu - io.KeyAlt = true; + if (shouldSendToImGui) + { + io.KeyAlt = true; + } + break; case Keys.LShiftKey: case Keys.RShiftKey: - io.KeyShift = true; + if (shouldSendToImGui) + { + io.KeyShift = true; + } + break; default: // Ignoring ALT key so we can do ALT+TAB or ALT+F4 etc. @@ -257,13 +346,8 @@ namespace ClickableTransparentOverlay } } - private void HookKeyPress(object sender, KeyPressEventArgs e) + private void ProcessKeyPress(KeyPressEventArgs e, bool shouldSendToImGui) { - if (!this.enable) - { - return; - } - var io = ImGui.GetIO(); // Ignoring Win/Super key so we can do Win+D or other stuff @@ -276,9 +360,87 @@ namespace ClickableTransparentOverlay if (io.WantTextInput || io.WantCaptureKeyboard) { - io.AddInputCharacter(e.KeyChar); + if (shouldSendToImGui) + { + io.AddInputCharacter(e.KeyChar); + } + else + { + this.PushMessage(HookControllerMessageType.KeyPress, e); + } + e.Handled = true; } } + + private void HookMouseUpExt(object sender, MouseEventExtArgs e) + { + if (this.enable) + { + this.ProcessMouseUpDown(e, false, false); + } + } + + private void HookMouseDownExt(object sender, MouseEventExtArgs e) + { + if (this.enable) + { + this.ProcessMouseUpDown(e, true, false); + } + } + + private void HookMouseMove(object sender, MouseEventArgs e) + { + if (!this.enable) + { + return; + } + + this.ProcessMouseMove(e, false); + } + + private void HookMouseWheelExt(object sender, MouseEventExtArgs e) + { + if (!this.enable) + { + return; + } + + this.ProcessMouseWheel(e, false); + } + + private void HookKeyUp(object sender, KeyEventArgs e) + { + this.ProcessKeyUp(e, true); + } + + private void HookKeyDown(object sender, KeyEventArgs e) + { + if (!this.enable) + { + return; + } + + this.ProcessKeyDown(e, true); + } + + private void HookKeyPress(object sender, KeyPressEventArgs e) + { + if (!this.enable) + { + return; + } + + this.ProcessKeyPress(e, false); + } + + private struct HookControllerMessage + { + public HookControllerMessageType Type { get; set; } + + public EventArgs E { get; set; } + + public bool MiscArg { get; set; } + } } } diff --git a/ClickableTransparentOverlay/Overlay.cs b/ClickableTransparentOverlay/Overlay.cs index 24a787c..1fa3bd4 100644 --- a/ClickableTransparentOverlay/Overlay.cs +++ b/ClickableTransparentOverlay/Overlay.cs @@ -245,6 +245,7 @@ namespace ClickableTransparentOverlay continue; } + hookController.PopMessages(); if (!window.Exists) { break;