diff --git a/ClickableTransparentOverlay.sln b/ClickableTransparentOverlay.sln
index c0b6511..1a56a0c 100644
--- a/ClickableTransparentOverlay.sln
+++ b/ClickableTransparentOverlay.sln
@@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.28307.168
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClickableTransparentOverlay", "ClickableTransparentOverlay\ClickableTransparentOverlay.csproj", "{F5CF8972-D0DA-4FC0-8796-E7C60101C8EA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DriverProgram", "DriverProgram\DriverProgram.csproj", "{370E1945-EF74-4618-A2DE-B5796AC3093E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -15,6 +17,10 @@ Global
{F5CF8972-D0DA-4FC0-8796-E7C60101C8EA}.Debug|x64.Build.0 = Debug|x64
{F5CF8972-D0DA-4FC0-8796-E7C60101C8EA}.Release|x64.ActiveCfg = Release|x64
{F5CF8972-D0DA-4FC0-8796-E7C60101C8EA}.Release|x64.Build.0 = Release|x64
+ {370E1945-EF74-4618-A2DE-B5796AC3093E}.Debug|x64.ActiveCfg = Debug|x64
+ {370E1945-EF74-4618-A2DE-B5796AC3093E}.Debug|x64.Build.0 = Debug|x64
+ {370E1945-EF74-4618-A2DE-B5796AC3093E}.Release|x64.ActiveCfg = Release|x64
+ {370E1945-EF74-4618-A2DE-B5796AC3093E}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj b/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj
index 5258ce8..ef2ab7a 100644
--- a/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj
+++ b/ClickableTransparentOverlay/ClickableTransparentOverlay.csproj
@@ -12,6 +12,8 @@
v4.7.2
512
true
+
+
true
@@ -39,6 +41,8 @@
x64
prompt
MinimumRecommendedRules.ruleset
+ true
+ false
bin\x64\Release\
@@ -48,6 +52,7 @@
x64
prompt
MinimumRecommendedRules.ruleset
+ true
@@ -56,27 +61,204 @@
..\packages\ImGui.NET.1.66.0\lib\netstandard2.0\ImGui.NET.dll
+
+ ..\packages\Microsoft.DotNet.PlatformAbstractions.2.0.3\lib\net45\Microsoft.DotNet.PlatformAbstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.DependencyModel.2.0.3\lib\net451\Microsoft.Extensions.DependencyModel.dll
+
+
+ ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll
+ True
+ True
+
+
+ ..\packages\NativeLibraryLoader.1.0.10\lib\netstandard2.0\NativeLibraryLoader.dll
+
+
+ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\SharpDX.4.0.1\lib\net45\SharpDX.dll
+
+
+ ..\packages\SharpDX.D3DCompiler.4.0.1\lib\net45\SharpDX.D3DCompiler.dll
+
+
+ ..\packages\SharpDX.Direct3D11.4.0.1\lib\net45\SharpDX.Direct3D11.dll
+
+
+ ..\packages\SharpDX.DXGI.4.0.1\lib\net45\SharpDX.DXGI.dll
+
+
+ ..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll
+ True
+ True
+
..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
+
+
+ ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll
+ True
+ True
+
+
+ ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
+
+
+ ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll
+ True
+ True
+
+
+
+ ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll
+ True
+ True
+
+
+ ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll
+ True
+ True
+
+
+ ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
+ True
+ True
+
+
+
+ ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
+ True
+ True
+
+
+ ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
+ True
+ True
+
+
+ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
+ True
+ True
+
+
+ ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll
+ True
+ True
+
+
+ ..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll
+ True
+ True
+
+
+ ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll
+ True
+ True
+
+
+ ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
+ True
+ True
+
..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+ ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
+ True
+ True
+
..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+ ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll
+ True
+ True
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
+ True
+ True
+
+
+ ..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll
+ True
+ True
+
+
-
+
+ ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll
+ True
+ True
+
+
+ ..\packages\Veldrid.4.5.0\lib\netstandard2.0\Veldrid.dll
+
+
+ ..\packages\Veldrid.MetalBindings.4.5.0\lib\netstandard2.0\Veldrid.MetalBindings.dll
+
+
+ ..\packages\Veldrid.OpenGLBindings.4.5.0\lib\netstandard2.0\Veldrid.OpenGLBindings.dll
+
+
+ ..\packages\Veldrid.SDL2.4.5.0\lib\netstandard2.0\Veldrid.SDL2.dll
+
+
+ ..\packages\Veldrid.StartupUtilities.4.5.0\lib\netstandard2.0\Veldrid.StartupUtilities.dll
+
+
+ ..\packages\Vk.1.0.21\lib\netstandard1.4\vk.dll
+
+
+
+
+
@@ -91,9 +273,16 @@
-
+
PreserveNewest
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
\ No newline at end of file
diff --git a/ClickableTransparentOverlay/HookController.cs b/ClickableTransparentOverlay/HookController.cs
new file mode 100644
index 0000000..bb54d7c
--- /dev/null
+++ b/ClickableTransparentOverlay/HookController.cs
@@ -0,0 +1,204 @@
+namespace ClickableTransparentOverlay
+{
+ using Gma.System.MouseKeyHook;
+ using ImGuiNET;
+ using System.Numerics;
+ using System.Windows.Forms;
+
+ public class HookController
+ {
+ private IKeyboardMouseEvents _hook;
+ private bool Enable;
+ private int WindowX;
+ private int WindowY;
+
+ public HookController(int x, int y)
+ {
+ WindowX = x;
+ WindowY = y;
+ Enable = true;
+ _hook = Hook.GlobalEvents();
+ }
+
+ public void EnableHooks()
+ {
+ _hook.KeyDown += _hook_KeyDown;
+ _hook.KeyUp += _hook_KeyUp;
+ _hook.KeyPress += _hook_KeyPress;
+
+ _hook.MouseDownExt += _hook_MouseDownExt;
+ _hook.MouseMove += _hook_MouseMove;
+ _hook.MouseUpExt += _hook_MouseUpExt;
+
+ _hook.MouseWheelExt += _hook_MouseWheelExt;
+ }
+
+ public void UpdateWindowPosition(int x, int y)
+ {
+ WindowX = x;
+ WindowY = y;
+ }
+
+ public void PauseHooks()
+ {
+ Enable = false;
+ }
+
+ public void ResumeHooks()
+ {
+ Enable = true;
+ }
+
+ private void _hook_MouseWheelExt(object sender, MouseEventExtArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+
+ ImGuiIOPtr io = ImGui.GetIO();
+ if (io.WantCaptureMouse)
+ {
+ io.MouseWheel = e.Delta / SystemInformation.MouseWheelScrollDelta;
+ e.Handled = true;
+ }
+ }
+
+ private void _hook_MouseUpExt(object sender, MouseEventExtArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+
+ ImGuiIOPtr io = ImGui.GetIO();
+
+ switch (e.Button)
+ {
+ case MouseButtons.Left:
+ io.MouseDown[0] = false;
+ break;
+ case MouseButtons.None:
+ // TODO: Find out what does this None mean
+ break;
+ case MouseButtons.Right:
+ io.MouseDown[1] = false;
+ break;
+ case MouseButtons.Middle:
+ io.MouseDown[2] = false;
+ break;
+ case MouseButtons.XButton1:
+ io.MouseDown[3] = false;
+ break;
+ case MouseButtons.XButton2:
+ io.MouseDown[4] = false;
+ break;
+ default:
+ // Make a Logger for the whole Overlay
+ break;
+ }
+
+ if (io.WantCaptureMouse)
+ {
+ e.Handled = true;
+ }
+ }
+
+ private void _hook_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+
+ ImGuiIOPtr io = ImGui.GetIO();
+ io.MousePos = new Vector2(e.X - WindowX, e.Y - WindowY);
+ // TODO: Show ImGUI Cursor/Hide ImGui Cursor
+ // ImGui.GetIO().MouseDrawCursor = true;
+ // Window32 API ShowCursor(false)
+ }
+
+ private void _hook_MouseDownExt(object sender, MouseEventExtArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+
+ ImGuiIOPtr io = ImGui.GetIO();
+ if (io.WantCaptureMouse)
+ {
+ switch (e.Button)
+ {
+ case MouseButtons.Left:
+ io.MouseDown[0] = true;
+ e.Handled = true;
+ break;
+ case MouseButtons.Right:
+ io.MouseDown[1] = true;
+ e.Handled = true;
+ break;
+ case MouseButtons.Middle:
+ io.MouseDown[2] = true;
+ e.Handled = true;
+ break;
+ case MouseButtons.XButton1:
+ io.MouseDown[3] = true;
+ e.Handled = true;
+ break;
+ case MouseButtons.XButton2:
+ io.MouseDown[4] = true;
+ e.Handled = true;
+ 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 _hook_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+ // TODO:
+ }
+
+ private void _hook_KeyUp(object sender, KeyEventArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+ // TODO:
+ }
+
+ private void _hook_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (!Enable)
+ {
+ return;
+ }
+ // TODO:
+ }
+
+ public void Dispose()
+ {
+ _hook.KeyDown -= _hook_KeyDown;
+ _hook.KeyUp -= _hook_KeyUp;
+ _hook.KeyPress -= _hook_KeyPress;
+
+ _hook.MouseDownExt -= _hook_MouseDownExt;
+ _hook.MouseMove -= _hook_MouseMove;
+ _hook.MouseUpExt -= _hook_MouseUpExt;
+
+ _hook.MouseWheelExt -= _hook_MouseWheelExt;
+ _hook.Dispose();
+ }
+ }
+}
diff --git a/ClickableTransparentOverlay/ImGuiController.cs b/ClickableTransparentOverlay/ImGuiController.cs
new file mode 100644
index 0000000..2bbc090
--- /dev/null
+++ b/ClickableTransparentOverlay/ImGuiController.cs
@@ -0,0 +1,490 @@
+namespace ClickableTransparentOverlay
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Numerics;
+ using System.Reflection;
+ using System.IO;
+ using Veldrid;
+ using System.Runtime.CompilerServices;
+ using ImGuiNET;
+
+ ///
+ /// A modified version of ImGui.NET.SampleProgram's ImGuiController.
+ /// Manages input for ImGui and handles rendering ImGui's DrawLists with Veldrid.
+ ///
+ public class ImGuiController : IDisposable
+ {
+ private GraphicsDevice _gd;
+ private bool _frameBegun;
+
+ // Veldrid objects
+ private DeviceBuffer _vertexBuffer;
+ private DeviceBuffer _indexBuffer;
+ private DeviceBuffer _projMatrixBuffer;
+ private Texture _fontTexture;
+ private TextureView _fontTextureView;
+ private Shader _vertexShader;
+ private Shader _fragmentShader;
+ private ResourceLayout _layout;
+ private ResourceLayout _textureLayout;
+ private Pipeline _pipeline;
+ private ResourceSet _mainResourceSet;
+ private ResourceSet _fontTextureResourceSet;
+
+ private IntPtr _fontAtlasID = (IntPtr)1;
+
+ private int _windowWidth;
+ private int _windowHeight;
+ private Vector2 _scaleFactor = Vector2.One;
+
+ // Image trackers
+ private readonly Dictionary _setsByView
+ = new Dictionary();
+ private readonly Dictionary _autoViewsByTexture
+ = new Dictionary();
+ private readonly Dictionary _viewsById = new Dictionary();
+ private readonly List _ownedResources = new List();
+ private int _lastAssignedID = 100;
+
+ ///
+ /// Constructs a new ImGuiController.
+ ///
+ public ImGuiController(GraphicsDevice gd, OutputDescription outputDescription, int width, int height, int fps)
+ {
+ _gd = gd;
+ _windowWidth = width;
+ _windowHeight = height;
+
+ IntPtr context = ImGui.CreateContext();
+ ImGui.SetCurrentContext(context);
+
+ ImGui.GetIO().Fonts.AddFontDefault();
+
+ CreateDeviceResources(gd, outputDescription);
+ SetKeyMappings();
+
+ SetPerFrameImGuiData(1f / fps);
+
+ ImGui.NewFrame();
+ _frameBegun = true;
+ }
+
+ public void WindowResized(int width, int height)
+ {
+ _windowWidth = width;
+ _windowHeight = height;
+ }
+
+ public void DestroyDeviceObjects()
+ {
+ Dispose();
+ }
+
+ public void CreateDeviceResources(GraphicsDevice gd, OutputDescription outputDescription)
+ {
+ _gd = gd;
+ ResourceFactory factory = gd.ResourceFactory;
+ _vertexBuffer = factory.CreateBuffer(new BufferDescription(10000, BufferUsage.VertexBuffer | BufferUsage.Dynamic));
+ _vertexBuffer.Name = "ImGui.NET Vertex Buffer";
+ _indexBuffer = factory.CreateBuffer(new BufferDescription(2000, BufferUsage.IndexBuffer | BufferUsage.Dynamic));
+ _indexBuffer.Name = "ImGui.NET Index Buffer";
+ RecreateFontDeviceTexture(gd);
+
+ _projMatrixBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
+ _projMatrixBuffer.Name = "ImGui.NET Projection Buffer";
+
+ byte[] vertexShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-vertex", ShaderStages.Vertex);
+ byte[] fragmentShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-frag", ShaderStages.Fragment);
+ _vertexShader = factory.CreateShader(new ShaderDescription(ShaderStages.Vertex, vertexShaderBytes, "VS"));
+ _fragmentShader = factory.CreateShader(new ShaderDescription(ShaderStages.Fragment, fragmentShaderBytes, "FS"));
+
+ VertexLayoutDescription[] vertexLayouts = new VertexLayoutDescription[]
+ {
+ new VertexLayoutDescription(
+ new VertexElementDescription("in_position", VertexElementSemantic.Position, VertexElementFormat.Float2),
+ new VertexElementDescription("in_texCoord", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2),
+ new VertexElementDescription("in_color", VertexElementSemantic.Color, VertexElementFormat.Byte4_Norm))
+ };
+
+ _layout = factory.CreateResourceLayout(new ResourceLayoutDescription(
+ new ResourceLayoutElementDescription("ProjectionMatrixBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex),
+ new ResourceLayoutElementDescription("MainSampler", ResourceKind.Sampler, ShaderStages.Fragment)));
+ _textureLayout = factory.CreateResourceLayout(new ResourceLayoutDescription(
+ new ResourceLayoutElementDescription("MainTexture", ResourceKind.TextureReadOnly, ShaderStages.Fragment)));
+
+ GraphicsPipelineDescription pd = new GraphicsPipelineDescription(
+ BlendStateDescription.SingleAlphaBlend,
+ new DepthStencilStateDescription(false, false, ComparisonKind.Always),
+ new RasterizerStateDescription(FaceCullMode.None, PolygonFillMode.Solid, FrontFace.Clockwise, false, true),
+ PrimitiveTopology.TriangleList,
+ new ShaderSetDescription(vertexLayouts, new[] { _vertexShader, _fragmentShader }),
+ new ResourceLayout[] { _layout, _textureLayout },
+ outputDescription);
+ _pipeline = factory.CreateGraphicsPipeline(ref pd);
+
+ _mainResourceSet = factory.CreateResourceSet(new ResourceSetDescription(_layout,
+ _projMatrixBuffer,
+ gd.PointSampler));
+
+ _fontTextureResourceSet = factory.CreateResourceSet(new ResourceSetDescription(_textureLayout, _fontTextureView));
+ }
+
+ ///
+ /// Gets or creates a handle for a texture to be drawn with ImGui.
+ /// Pass the returned handle to Image() or ImageButton().
+ ///
+ public IntPtr GetOrCreateImGuiBinding(ResourceFactory factory, TextureView textureView)
+ {
+ if (!_setsByView.TryGetValue(textureView, out ResourceSetInfo rsi))
+ {
+ ResourceSet resourceSet = factory.CreateResourceSet(new ResourceSetDescription(_textureLayout, textureView));
+ rsi = new ResourceSetInfo(GetNextImGuiBindingID(), resourceSet);
+
+ _setsByView.Add(textureView, rsi);
+ _viewsById.Add(rsi.ImGuiBinding, rsi);
+ _ownedResources.Add(resourceSet);
+ }
+
+ return rsi.ImGuiBinding;
+ }
+
+ private IntPtr GetNextImGuiBindingID()
+ {
+ int newID = _lastAssignedID++;
+ return (IntPtr)newID;
+ }
+
+ ///
+ /// Gets or creates a handle for a texture to be drawn with ImGui.
+ /// Pass the returned handle to Image() or ImageButton().
+ ///
+ public IntPtr GetOrCreateImGuiBinding(ResourceFactory factory, Texture texture)
+ {
+ if (!_autoViewsByTexture.TryGetValue(texture, out TextureView textureView))
+ {
+ textureView = factory.CreateTextureView(texture);
+ _autoViewsByTexture.Add(texture, textureView);
+ _ownedResources.Add(textureView);
+ }
+
+ return GetOrCreateImGuiBinding(factory, textureView);
+ }
+
+ ///
+ /// Retrieves the shader texture binding for the given helper handle.
+ ///
+ public ResourceSet GetImageResourceSet(IntPtr imGuiBinding)
+ {
+ if (!_viewsById.TryGetValue(imGuiBinding, out ResourceSetInfo tvi))
+ {
+ throw new InvalidOperationException("No registered ImGui binding with id " + imGuiBinding.ToString());
+ }
+
+ return tvi.ResourceSet;
+ }
+
+ public void ClearCachedImageResources()
+ {
+ foreach (IDisposable resource in _ownedResources)
+ {
+ resource.Dispose();
+ }
+
+ _ownedResources.Clear();
+ _setsByView.Clear();
+ _viewsById.Clear();
+ _autoViewsByTexture.Clear();
+ _lastAssignedID = 100;
+ }
+
+ private byte[] LoadEmbeddedShaderCode(ResourceFactory factory, string name, ShaderStages stage)
+ {
+ switch (factory.BackendType)
+ {
+ case GraphicsBackend.Direct3D11:
+ {
+ string resourceName = name + ".hlsl.bytes";
+ return GetEmbeddedResourceBytes(resourceName);
+ }
+ case GraphicsBackend.OpenGL:
+ {
+ string resourceName = name + ".glsl";
+ return GetEmbeddedResourceBytes(resourceName);
+ }
+ case GraphicsBackend.Vulkan:
+ {
+ string resourceName = name + ".spv";
+ return GetEmbeddedResourceBytes(resourceName);
+ }
+ case GraphicsBackend.Metal:
+ {
+ string resourceName = name + ".metallib";
+ return GetEmbeddedResourceBytes(resourceName);
+ }
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ private byte[] GetEmbeddedResourceBytes(string resourceName)
+ {
+ Assembly assembly = typeof(ImGuiController).Assembly;
+ using (Stream s = assembly.GetManifestResourceStream(resourceName))
+ {
+ byte[] ret = new byte[s.Length];
+ s.Read(ret, 0, (int)s.Length);
+ return ret;
+ }
+ }
+
+ ///
+ /// Recreates the device texture used to render text.
+ ///
+ public unsafe void RecreateFontDeviceTexture(GraphicsDevice gd)
+ {
+ ImGuiIOPtr io = ImGui.GetIO();
+ // Build
+ byte* pixels;
+ int width, height, bytesPerPixel;
+ io.Fonts.GetTexDataAsRGBA32(out pixels, out width, out height, out bytesPerPixel);
+ // Store our identifier
+ io.Fonts.SetTexID(_fontAtlasID);
+
+ _fontTexture = gd.ResourceFactory.CreateTexture(TextureDescription.Texture2D(
+ (uint)width,
+ (uint)height,
+ 1,
+ 1,
+ PixelFormat.R8_G8_B8_A8_UNorm,
+ TextureUsage.Sampled));
+ _fontTexture.Name = "ImGui.NET Font Texture";
+ gd.UpdateTexture(
+ _fontTexture,
+ (IntPtr)pixels,
+ (uint)(bytesPerPixel * width * height),
+ 0,
+ 0,
+ 0,
+ (uint)width,
+ (uint)height,
+ 1,
+ 0,
+ 0);
+ _fontTextureView = gd.ResourceFactory.CreateTextureView(_fontTexture);
+
+ io.Fonts.ClearTexData();
+ }
+
+ ///
+ /// Renders the ImGui draw list data.
+ /// This method requires a because it may create new DeviceBuffers if the size of vertex
+ /// or index data has increased beyond the capacity of the existing buffers.
+ /// A is needed to submit drawing and resource update commands.
+ ///
+ public void Render(GraphicsDevice gd, CommandList cl)
+ {
+ if (_frameBegun)
+ {
+ _frameBegun = false;
+ ImGui.Render();
+ RenderImDrawData(ImGui.GetDrawData(), gd, cl);
+ }
+ }
+
+ ///
+ /// Initilizes a new frame
+ ///
+ public void InitlizeFrame(float deltaSeconds)
+ {
+ if (_frameBegun)
+ {
+ ImGui.Render();
+ }
+
+ SetPerFrameImGuiData(deltaSeconds);
+
+ _frameBegun = true;
+ ImGui.NewFrame();
+ }
+
+ ///
+ /// Sets per-frame data based on the associated window.
+ /// This is called by Update(float).
+ ///
+ private void SetPerFrameImGuiData(float deltaSeconds)
+ {
+ ImGuiIOPtr io = ImGui.GetIO();
+ io.DisplaySize = new Vector2(
+ _windowWidth / _scaleFactor.X,
+ _windowHeight / _scaleFactor.Y);
+ io.DisplayFramebufferScale = _scaleFactor;
+ io.DeltaTime = deltaSeconds; // DeltaTime is in seconds.
+ }
+
+ private static void SetKeyMappings()
+ {
+ ImGuiIOPtr io = ImGui.GetIO();
+ io.KeyMap[(int)ImGuiKey.Tab] = (int)Key.Tab;
+ io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Key.Left;
+ io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Key.Right;
+ io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Key.Up;
+ io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Key.Down;
+ io.KeyMap[(int)ImGuiKey.PageUp] = (int)Key.PageUp;
+ io.KeyMap[(int)ImGuiKey.PageDown] = (int)Key.PageDown;
+ io.KeyMap[(int)ImGuiKey.Home] = (int)Key.Home;
+ io.KeyMap[(int)ImGuiKey.End] = (int)Key.End;
+ io.KeyMap[(int)ImGuiKey.Delete] = (int)Key.Delete;
+ io.KeyMap[(int)ImGuiKey.Backspace] = (int)Key.BackSpace;
+ io.KeyMap[(int)ImGuiKey.Enter] = (int)Key.Enter;
+ io.KeyMap[(int)ImGuiKey.Escape] = (int)Key.Escape;
+ io.KeyMap[(int)ImGuiKey.A] = (int)Key.A;
+ io.KeyMap[(int)ImGuiKey.C] = (int)Key.C;
+ io.KeyMap[(int)ImGuiKey.V] = (int)Key.V;
+ io.KeyMap[(int)ImGuiKey.X] = (int)Key.X;
+ io.KeyMap[(int)ImGuiKey.Y] = (int)Key.Y;
+ io.KeyMap[(int)ImGuiKey.Z] = (int)Key.Z;
+ }
+
+ private void RenderImDrawData(ImDrawDataPtr draw_data, GraphicsDevice gd, CommandList cl)
+ {
+ uint vertexOffsetInVertices = 0;
+ uint indexOffsetInElements = 0;
+
+ if (draw_data.CmdListsCount == 0)
+ {
+ return;
+ }
+
+ uint totalVBSize = (uint)(draw_data.TotalVtxCount * Unsafe.SizeOf());
+ if (totalVBSize > _vertexBuffer.SizeInBytes)
+ {
+ gd.DisposeWhenIdle(_vertexBuffer);
+ _vertexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalVBSize * 1.5f), BufferUsage.VertexBuffer | BufferUsage.Dynamic));
+ }
+
+ uint totalIBSize = (uint)(draw_data.TotalIdxCount * sizeof(ushort));
+ if (totalIBSize > _indexBuffer.SizeInBytes)
+ {
+ gd.DisposeWhenIdle(_indexBuffer);
+ _indexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalIBSize * 1.5f), BufferUsage.IndexBuffer | BufferUsage.Dynamic));
+ }
+
+ for (int i = 0; i < draw_data.CmdListsCount; i++)
+ {
+ ImDrawListPtr cmd_list = draw_data.CmdListsRange[i];
+
+ cl.UpdateBuffer(
+ _vertexBuffer,
+ vertexOffsetInVertices * (uint)Unsafe.SizeOf(),
+ cmd_list.VtxBuffer.Data,
+ (uint)(cmd_list.VtxBuffer.Size * Unsafe.SizeOf()));
+
+ cl.UpdateBuffer(
+ _indexBuffer,
+ indexOffsetInElements * sizeof(ushort),
+ cmd_list.IdxBuffer.Data,
+ (uint)(cmd_list.IdxBuffer.Size * sizeof(ushort)));
+
+ vertexOffsetInVertices += (uint)cmd_list.VtxBuffer.Size;
+ indexOffsetInElements += (uint)cmd_list.IdxBuffer.Size;
+ }
+
+ // Setup orthographic projection matrix into our constant buffer
+ ImGuiIOPtr io = ImGui.GetIO();
+ Matrix4x4 mvp = Matrix4x4.CreateOrthographicOffCenter(
+ 0f,
+ io.DisplaySize.X,
+ io.DisplaySize.Y,
+ 0.0f,
+ -1.0f,
+ 1.0f);
+
+ _gd.UpdateBuffer(_projMatrixBuffer, 0, ref mvp);
+
+ cl.SetVertexBuffer(0, _vertexBuffer);
+ cl.SetIndexBuffer(_indexBuffer, IndexFormat.UInt16);
+ cl.SetPipeline(_pipeline);
+ cl.SetGraphicsResourceSet(0, _mainResourceSet);
+
+ draw_data.ScaleClipRects(io.DisplayFramebufferScale);
+
+ // Render command lists
+ int vtx_offset = 0;
+ int idx_offset = 0;
+ for (int n = 0; n < draw_data.CmdListsCount; n++)
+ {
+ ImDrawListPtr cmd_list = draw_data.CmdListsRange[n];
+ for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++)
+ {
+ ImDrawCmdPtr pcmd = cmd_list.CmdBuffer[cmd_i];
+ if (pcmd.UserCallback != IntPtr.Zero)
+ {
+ throw new NotImplementedException();
+ }
+ else
+ {
+ if (pcmd.TextureId != IntPtr.Zero)
+ {
+ if (pcmd.TextureId == _fontAtlasID)
+ {
+ cl.SetGraphicsResourceSet(1, _fontTextureResourceSet);
+ }
+ else
+ {
+ cl.SetGraphicsResourceSet(1, GetImageResourceSet(pcmd.TextureId));
+ }
+ }
+
+ cl.SetScissorRect(
+ 0,
+ (uint)pcmd.ClipRect.X,
+ (uint)pcmd.ClipRect.Y,
+ (uint)(pcmd.ClipRect.Z - pcmd.ClipRect.X),
+ (uint)(pcmd.ClipRect.W - pcmd.ClipRect.Y));
+
+ cl.DrawIndexed(pcmd.ElemCount, 1, (uint)idx_offset, vtx_offset, 0);
+ }
+
+ idx_offset += (int)pcmd.ElemCount;
+ }
+ vtx_offset += cmd_list.VtxBuffer.Size;
+ }
+ }
+
+ ///
+ /// Frees all graphics resources used by the renderer.
+ ///
+ public void Dispose()
+ {
+ _vertexBuffer.Dispose();
+ _indexBuffer.Dispose();
+ _projMatrixBuffer.Dispose();
+ _fontTexture.Dispose();
+ _fontTextureView.Dispose();
+ _vertexShader.Dispose();
+ _fragmentShader.Dispose();
+ _layout.Dispose();
+ _textureLayout.Dispose();
+ _pipeline.Dispose();
+ _mainResourceSet.Dispose();
+
+ foreach (IDisposable resource in _ownedResources)
+ {
+ resource.Dispose();
+ }
+ }
+
+ private struct ResourceSetInfo
+ {
+ public readonly IntPtr ImGuiBinding;
+ public readonly ResourceSet ResourceSet;
+
+ public ResourceSetInfo(IntPtr imGuiBinding, ResourceSet resourceSet)
+ {
+ ImGuiBinding = imGuiBinding;
+ ResourceSet = resourceSet;
+ }
+ }
+ }
+}
diff --git a/ClickableTransparentOverlay/Overlay.cs b/ClickableTransparentOverlay/Overlay.cs
new file mode 100644
index 0000000..59a83e7
--- /dev/null
+++ b/ClickableTransparentOverlay/Overlay.cs
@@ -0,0 +1,152 @@
+namespace ClickableTransparentOverlay
+{
+ using System;
+ using System.Numerics;
+ using System.Threading;
+ using System.Windows.Forms;
+ using Veldrid;
+ using Veldrid.Sdl2;
+ using Veldrid.StartupUtilities;
+
+ public class Overlay
+ {
+ private static Sdl2Window _window;
+ private static GraphicsDevice _gd;
+ private static CommandList _cl;
+ private static ImGuiController _im_controller;
+ private static HookController _hook_controller;
+ private static Thread _ui_thread;
+ public event EventHandler SubmitUI;
+
+ // UI State
+ private static Vector4 _clearColor;
+ private static Vector2 _future_pos;
+ private static Vector2 _future_size;
+ private static int _fps;
+ private static bool _is_visible;
+ private static bool _require_resize;
+ private static bool _start_resizing;
+ private static object _resize_thread_lock;
+
+ public Overlay(int x, int y, int width, int height, int fps)
+ {
+ _clearColor = new Vector4(0.00f, 0.00f, 0.00f, 0.00f);
+ _fps = fps;
+ _is_visible = true;
+ // Stuff related to (thread safe) resizing of SDL2Window
+ _require_resize = false;
+ _start_resizing = false;
+ _resize_thread_lock = new object();
+ _future_size = Vector2.Zero;
+ _future_pos = Vector2.Zero;
+
+ _window = new Sdl2Window("Overlay", x, x, width, height, SDL_WindowFlags.Borderless | SDL_WindowFlags.AlwaysOnTop | SDL_WindowFlags.Resizable | SDL_WindowFlags.SkipTaskbar, true);
+ _gd = VeldridStartup.CreateGraphicsDevice(_window, new GraphicsDeviceOptions(true, null, true), GraphicsBackend.Direct3D11);
+ WinApi.EnableTransparent(_window.Handle);
+ _window.Resized += () =>
+ {
+ _gd.MainSwapchain.Resize((uint)_window.Width, (uint)_window.Height);
+ _im_controller.WindowResized(_window.Width, _window.Height);
+ lock (_resize_thread_lock)
+ {
+ _require_resize = false;
+ _start_resizing = false;
+ }
+ };
+
+ _cl = _gd.ResourceFactory.CreateCommandList();
+ _im_controller = new ImGuiController(_gd, _gd.MainSwapchain.Framebuffer.OutputDescription, _window.Width, _window.Height, _fps);
+ _ui_thread = new Thread(WhileLoop);
+ _hook_controller = new HookController(_window.X, _window.Y);
+ }
+
+ public void Run()
+ {
+ _ui_thread.Start();
+ _hook_controller.EnableHooks();
+ WinApi.HideConsoleWindow();
+ Application.Run(new ApplicationContext());
+ }
+
+ public void Dispose()
+ {
+ _is_visible = false;
+ _window.Close();
+ _ui_thread.Join();
+ _gd.WaitForIdle();
+ _im_controller.Dispose();
+ _cl.Dispose();
+ _gd.Dispose();
+ _hook_controller.Dispose();
+ WinApi.ShowConsoleWindow();
+ Console.WriteLine("All Overlay resources are cleared.");
+ }
+
+ public void ResizeWindow(int x, int y, int width, int height)
+ {
+ // TODO: Add lock here to pause the ImGUI thread before resizing
+ // TODO: Test mouse/imgui UI in a non-full screen overlay
+ _future_pos.X = x;
+ _future_pos.Y = y;
+ _future_size.X = width;
+ _future_size.Y = height;
+ // TODO: move it to _window.Moved
+ _hook_controller.UpdateWindowPosition(x, y);
+ _require_resize = true;
+ }
+
+ public void ShowWindow()
+ {
+ // TODO: Test this feature for ImGui Display and Hooks
+ _hook_controller.ResumeHooks();
+ _is_visible = true;
+ }
+
+ public void HideWindow()
+ {
+ //TODO: Test this feature for ImGui Display and Hooks
+ _hook_controller.PauseHooks();
+ _is_visible = false;
+ }
+
+ private void WhileLoop()
+ {
+ while (_window.Exists)
+ {
+ lock (_resize_thread_lock)
+ {
+ if (_require_resize)
+ {
+ if (!_start_resizing)
+ {
+ Sdl2Native.SDL_SetWindowPosition(_window.SdlWindowHandle, (int)_future_pos.X, (int)_future_pos.Y);
+ Sdl2Native.SDL_SetWindowSize(_window.SdlWindowHandle, (int)_future_size.X, (int)_future_size.Y);
+ _start_resizing = true;
+ }
+ continue;
+ }
+ }
+
+ if (!_window.Exists)
+ {
+ break;
+ }
+
+ _im_controller.InitlizeFrame(1f / _fps);
+
+ if (_is_visible)
+ {
+ SubmitUI?.Invoke(this, new EventArgs());
+ }
+
+ _cl.Begin();
+ _cl.SetFramebuffer(_gd.MainSwapchain.Framebuffer);
+ _cl.ClearColorTarget(0, new RgbaFloat(_clearColor.X, _clearColor.Y, _clearColor.Z, _clearColor.W));
+ _im_controller.Render(_gd, _cl);
+ _cl.End();
+ _gd.SubmitCommands(_cl);
+ _gd.SwapBuffers(_gd.MainSwapchain);
+ }
+ }
+ }
+}
diff --git a/ClickableTransparentOverlay/WinApi.cs b/ClickableTransparentOverlay/WinApi.cs
new file mode 100644
index 0000000..198f1c1
--- /dev/null
+++ b/ClickableTransparentOverlay/WinApi.cs
@@ -0,0 +1,51 @@
+namespace ClickableTransparentOverlay
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ public static class WinApi
+ {
+ private const int GWL_EXSTYLE = -20;
+ private const int WS_EX_LAYERED = 0x80000;
+ private const int WS_EX_TRANSPARENT = 0x20;
+ private const int LWA_ALPHA = 0x02;
+ private const int LWA_COLORKEY = 0x01;
+
+ private const int SW_HIDE = 0x00;
+ private const int SW_SHOW = 0x05;
+
+ public static void EnableTransparent(IntPtr handle)
+ {
+ int windowLong = GetWindowLong(handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT;
+ SetWindowLong(handle, GWL_EXSTYLE, new IntPtr(windowLong));
+ SetLayeredWindowAttributes(handle, 0, 255, LWA_ALPHA | LWA_COLORKEY);
+ }
+
+ public static void HideConsoleWindow()
+ {
+ var handle = GetConsoleWindow();
+ ShowWindow(handle, SW_HIDE);
+ }
+
+ public static void ShowConsoleWindow()
+ {
+ var handle = GetConsoleWindow();
+ ShowWindow(handle, SW_SHOW);
+ }
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern bool SetLayeredWindowAttributes(IntPtr hWnd, uint crKey, byte bAlpha, uint dwFlags);
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr GetConsoleWindow();
+
+ [DllImport("user32.dll")]
+ static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
+ }
+}
diff --git a/ClickableTransparentOverlay/app.config b/ClickableTransparentOverlay/app.config
index 8e3c0a1..a1b0aab 100644
--- a/ClickableTransparentOverlay/app.config
+++ b/ClickableTransparentOverlay/app.config
@@ -10,6 +10,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/ClickableTransparentOverlay/deps/cimgui.dll b/ClickableTransparentOverlay/cimgui.dll
similarity index 100%
rename from ClickableTransparentOverlay/deps/cimgui.dll
rename to ClickableTransparentOverlay/cimgui.dll
diff --git a/ClickableTransparentOverlay/packages.config b/ClickableTransparentOverlay/packages.config
index 63832ef..c0042df 100644
--- a/ClickableTransparentOverlay/packages.config
+++ b/ClickableTransparentOverlay/packages.config
@@ -1,8 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DriverProgram/App.config b/DriverProgram/App.config
new file mode 100644
index 0000000..37a645e
--- /dev/null
+++ b/DriverProgram/App.config
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DriverProgram/DriverProgram.csproj b/DriverProgram/DriverProgram.csproj
new file mode 100644
index 0000000..93ccb88
--- /dev/null
+++ b/DriverProgram/DriverProgram.csproj
@@ -0,0 +1,95 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {370E1945-EF74-4618-A2DE-B5796AC3093E}
+ Exe
+ DriverProgram
+ DriverProgram
+ v4.7.2
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+ true
+
+
+
+ ..\packages\ImGui.NET.1.66.0\lib\netstandard2.0\ImGui.NET.dll
+
+
+
+ ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
+
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {f5cf8972-d0da-4fc0-8796-e7c60101c8ea}
+ ClickableTransparentOverlay
+
+
+
+
\ No newline at end of file
diff --git a/DriverProgram/Program.cs b/DriverProgram/Program.cs
new file mode 100644
index 0000000..37d98b6
--- /dev/null
+++ b/DriverProgram/Program.cs
@@ -0,0 +1,42 @@
+namespace DriverProgram
+{
+ using ClickableTransparentOverlay;
+ using ImGuiNET;
+ using System.Threading;
+
+ class Program
+ {
+ private static Overlay demo;
+ private static int Width;
+ private static int Height;
+ private static int Fps;
+ static void Main(string[] args)
+ {
+ Width = int.Parse(System.IO.File.ReadAllText("config/width.txt"));
+ Height = int.Parse(System.IO.File.ReadAllText("config/height.txt"));
+ Fps = int.Parse(System.IO.File.ReadAllText("config/fps.txt"));
+ var EndDemo = new Thread(DistroyDemo);
+ EndDemo.Start();
+ StartDemo();
+ }
+
+ public static void StartDemo()
+ {
+ demo = new Overlay(0, 0, Width, Height, Fps);
+ demo.SubmitUI += (object sender, System.EventArgs e) => ImGui.ShowDemoWindow();
+ demo.Run();
+ }
+
+ public static void DistroyDemo()
+ {
+ Thread.Sleep(10000);
+ demo.ResizeWindow(100, 100, 1024, 1024);
+ Thread.Sleep(10000);
+ demo.HideWindow();
+ Thread.Sleep(10000);
+ demo.ShowWindow();
+ Thread.Sleep(10000);
+ demo.Dispose();
+ }
+ }
+}
diff --git a/DriverProgram/Properties/AssemblyInfo.cs b/DriverProgram/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c1cd08a
--- /dev/null
+++ b/DriverProgram/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DriverProgram")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DriverProgram")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("370e1945-ef74-4618-a2de-b5796ac3093e")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/DriverProgram/packages.config b/DriverProgram/packages.config
new file mode 100644
index 0000000..c8a4541
--- /dev/null
+++ b/DriverProgram/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 95a7dd2..8876602 100644
--- a/README.md
+++ b/README.md
@@ -6,3 +6,5 @@ A library for creating clickable transparent overlay using Win32 API and ImGui.N
* ImGui.NET
* Veldrid
* MouseKeyHook
+* .NET Core 2.0
+* .NET Framework 4.7.2
\ No newline at end of file