You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ClickableTransparentOverlay/ClickableTransparentOverlay/Overlay.cs

221 lines
7.5 KiB

using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading;
using Coroutine;
using Veldrid;
using Veldrid.ImageSharp;
using Veldrid.Sdl2;
using Veldrid.StartupUtilities;
namespace ClickableTransparentOverlay
{
// TODO: Implement overlay info, warn, error logger.
/// <summary>
/// A class to create clickable transparent overlay.
/// </summary>
public static class Overlay
{
private readonly static Sdl2Window window;
private readonly static GraphicsDevice graphicsDevice;
private readonly static CommandList commandList;
private readonly static ImGuiController imController;
private readonly static Vector4 clearColor;
private readonly static Dictionary<string, Texture> loadedImages;
private static bool terminal = true;
/// <summary>
/// Initializes a new instance of the <see cref="Overlay"/> class.
/// </summary>
/// <param name="x">
/// x position of the overlay.
/// </param>
/// <param name="y">
/// y position of the overlay.
/// </param>
/// <param name="width">
/// width of the overlay.
/// </param>
/// <param name="height">
/// height of the Overlay.
/// </param>
/// <param name="fps">
/// fps of the overlay.
/// </param>
/// <param name="debug">
/// In this mode, overlay will not hide the Console.
/// </param>
static Overlay()
{
clearColor = new Vector4(0.00f, 0.00f, 0.00f, 0.00f);
loadedImages = new Dictionary<string, Texture>();
window = new Sdl2Window(
"Overlay",
0,
0,
2560,
1440,
SDL_WindowFlags.Borderless |
SDL_WindowFlags.AlwaysOnTop |
SDL_WindowFlags.SkipTaskbar,
false);
graphicsDevice = VeldridStartup.CreateDefaultD3D11GraphicsDevice(
new GraphicsDeviceOptions(false, null, true),
window);
commandList = graphicsDevice.ResourceFactory.CreateCommandList();
imController = new ImGuiController(
graphicsDevice,
graphicsDevice.MainSwapchain.Framebuffer.OutputDescription,
window.Width,
window.Height);
window.Resized += () =>
{
graphicsDevice.MainSwapchain.Resize((uint)window.Width, (uint)window.Height);
imController.WindowResized(window.Width, window.Height);
};
NativeMethods.InitTransparency(window.Handle);
NativeMethods.SetOverlayClickable(window.Handle, false);
}
public static void RunInfiniteLoop()
{
DateTime previous = DateTime.Now;
DateTime current;
TimeSpan interval;
float sec;
while (window.Exists && !Close)
{
InputSnapshot snapshot = window.PumpEvents();
if (!window.Exists) { break; }
current = DateTime.Now;
interval = current - previous;
sec = (float)interval.TotalSeconds;
previous = current;
imController.Update(sec > 0 ? sec : 0.001f, snapshot, window.Handle);
CoroutineHandler.Tick(interval.TotalSeconds);
if (Visible)
{
CoroutineHandler.RaiseEvent(OnRender);
}
commandList.Begin();
commandList.SetFramebuffer(graphicsDevice.MainSwapchain.Framebuffer);
commandList.ClearColorTarget(0, new RgbaFloat(clearColor.X, clearColor.Y, clearColor.Z, clearColor.W));
imController.Render(graphicsDevice, commandList);
commandList.End();
graphicsDevice.SubmitCommands(commandList);
graphicsDevice.SwapBuffers(graphicsDevice.MainSwapchain);
}
Dispose();
}
/// <summary>
/// To submit ImGui code for generating the UI.
/// </summary>
public static Event OnRender = new Event();
/// <summary>
/// Safely Closes the Overlay.
/// Doesn't matter if you set it to true multiple times.
/// </summary>
public static bool Close { get; set; } = false;
/// <summary>
/// Makes the overlay visible or invisible. Invisible Overlay
/// will not call OnRender coroutines, however time based
/// coroutines are still called.
/// </summary>
public static bool Visible { get; set; } = true;
public static bool TerminalWindow
{
get => terminal;
set
{
if (value != terminal)
{
NativeMethods.SetConsoleWindow(value);
}
terminal = value;
}
}
/// <summary>
/// Gets or sets the position of the overlay window.
/// </summary>
public static Point Position
{
get
{
return new Point(window.X, window.Y);
}
set
{
Sdl2Native.SDL_SetWindowPosition(window.SdlWindowHandle, value.X, value.Y);
}
}
/// <summary>
/// Gets or sets the size of the overlay window.
/// </summary>
public static Point Size
{
get
{
return new Point(window.Width, window.Height);
}
set
{
Sdl2Native.SDL_SetWindowSize(window.SdlWindowHandle, value.X, value.Y);
}
}
/// <summary>
/// Adds the image to the Graphic Device as a texture.
/// Then returns the pointer of the added texture. It also
/// cache the image internally rather than creating a new texture on every call,
/// so this function can be called multiple times per frame.
/// </summary>
/// <param name="filePath">
/// Path to the image on disk. If the image is loaded in the memory
/// save it on the disk before sending to this function. Reason for this
/// is to cache the Image Texture using filePath as the key.
/// </param>
/// <returns>
/// A pointer to the Texture in the Graphic Device.
/// </returns>
public static IntPtr AddOrGetImagePointer(string filePath)
{
if (!loadedImages.TryGetValue(filePath, out Texture texture))
{
ImageSharpTexture imgSharpTexture = new ImageSharpTexture(filePath);
texture = imgSharpTexture.CreateDeviceTexture(graphicsDevice, graphicsDevice.ResourceFactory);
loadedImages.Add(filePath, texture);
}
return imController.GetOrCreateImGuiBinding(graphicsDevice.ResourceFactory, texture);
}
/// <summary>
/// Free all resources acquired by the overlay.
/// </summary>
private static void Dispose()
{
window.Close();
while (window.Exists)
{
Thread.Sleep(1);
}
graphicsDevice.WaitForIdle();
imController.Dispose();
commandList.Dispose();
graphicsDevice.Dispose();
loadedImages.Clear();
NativeMethods.SetConsoleWindow(true);
}
}
}