From 7bec94ccb5a005a71e05ea9c3d5d36d20bebf36c Mon Sep 17 00:00:00 2001 From: Xab3r Date: Sat, 21 Jun 2025 22:55:40 +0200 Subject: [PATCH] Added ShowInTaskbar/NoActivate and made IsClickable non-static --- ClickableTransparentOverlay/Overlay.cs | 47 ++++++--- ClickableTransparentOverlay/Win32/User32.cs | 108 +++++++++++++++++++- ClickableTransparentOverlay/Win32/Utils.cs | 41 ++++---- 3 files changed, 161 insertions(+), 35 deletions(-) diff --git a/ClickableTransparentOverlay/Overlay.cs b/ClickableTransparentOverlay/Overlay.cs index 9465c6d..70f06f1 100644 --- a/ClickableTransparentOverlay/Overlay.cs +++ b/ClickableTransparentOverlay/Overlay.cs @@ -30,6 +30,8 @@ public abstract class Overlay : IDisposable private readonly Format format; private readonly int initialWindowWidth; private readonly int initialWindowHeight; + private readonly Dictionary loadedTexturesPtrs; + private readonly ConcurrentQueue fontUpdates; private WNDCLASSEX wndClass; @@ -42,21 +44,20 @@ public abstract class Overlay : IDisposable private ID3D11DeviceContext deviceContext; private IDXGISwapChain swapChain; private ID3D11Texture2D backBuffer; - private ID3D11RenderTargetView renderView; + private ID3D11RenderTargetView? renderView; private ImGuiRenderer renderer; private ImGuiInputHandler inputhandler; - private bool _disposedValue; + private bool disposedValue; private IntPtr selfPointer; private Thread renderThread; private volatile CancellationTokenSource cancellationTokenSource; private volatile bool overlayIsReady; private int fpslimit; - - private Dictionary loadedTexturesPtrs; - - private readonly ConcurrentQueue fontUpdates; + private bool isClickable; + private bool noActivate; + private bool showInTaskbar = true; //that is the default state of the window #region Constructors @@ -138,9 +139,6 @@ public Overlay(string windowTitle, int windowWidth, int windowHeight) : this(win /// /// should the overlay scale with windows scale value or not. /// - /// - /// vsync is enabled if true otherwise disabled. - /// /// /// width to use when creating the clickable transparent overlay window /// @@ -153,7 +151,7 @@ public Overlay(string windowTitle, bool DPIAware, int windowWidth, int windowHei this.initialWindowHeight = windowHeight; this.VSync = false; this.FPSLimit = 60; - this._disposedValue = false; + this.disposedValue = false; this.overlayIsReady = false; this.title = windowTitle; this.cancellationTokenSource = new(); @@ -165,6 +163,21 @@ public Overlay(string windowTitle, bool DPIAware, int windowWidth, int windowHei User32.SetProcessDPIAware(); } } + + /// + /// Gets or sets a value indicating whether the window should be click-through (i.e., not interactable). + /// + public bool IsClickable { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the window should appear in the taskbar. + /// + public bool ShowInTaskbar { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the window should NOT be activated when clicked + /// + public bool NoActivate { get; set; } #endregion @@ -467,7 +480,7 @@ public bool RemoveImage(string key) protected virtual void Dispose(bool disposing) { - if (this._disposedValue) + if (this.disposedValue) { return; } @@ -506,7 +519,7 @@ protected virtual void Dispose(bool disposing) this.selfPointer = IntPtr.Zero; } - this._disposedValue = true; + this.disposedValue = true; } /// @@ -535,7 +548,9 @@ private void RunInfiniteLoop(CancellationToken token) currentTimeSec = stopwatch.ElapsedTicks / (float)Stopwatch.Frequency; stopwatch.Restart(); this.window.PumpEvents(); - Utils.SetOverlayClickable(this.window.Handle, this.inputhandler.Update()); + Utils.SetOverlayClickable(this.window.Handle, this.inputhandler.Update(), ref isClickable); + Utils.SetShowInTaskbar(this.window.Handle, ShowInTaskbar, ref showInTaskbar); + Utils.SetNoActivate(this.window.Handle, NoActivate, ref noActivate); this.renderer.Update(currentTimeSec, () => { Render(); }); this.deviceContext.OMSetRenderTargets(renderView); this.deviceContext.ClearRenderTargetView(renderView, clearColor); @@ -657,6 +672,7 @@ private async Task InitializeResources() await this.PostInitialized(); User32.ShowWindow(this.window.Handle, ShowWindowCommand.Show); Utils.InitTransparency(this.window.Handle); + Utils.SetOverlayClickable(this.window.Handle, true, ref isClickable); } private bool ProcessMessage(WindowMessage msg, UIntPtr wParam, IntPtr lParam) @@ -693,6 +709,11 @@ private IntPtr WndProc(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam) { if (this.overlayIsReady) { + if (NoActivate && msg == User32.WM_MOUSEACTIVATE) + { + return new IntPtr(User32.MA_NOACTIVATE); + } + if (this.inputhandler.ProcessMessage((WindowMessage)msg, wParam, lParam) || this.ProcessMessage((WindowMessage)msg, wParam, lParam)) { diff --git a/ClickableTransparentOverlay/Win32/User32.cs b/ClickableTransparentOverlay/Win32/User32.cs index dfce2e7..ce6c89b 100644 --- a/ClickableTransparentOverlay/Win32/User32.cs +++ b/ClickableTransparentOverlay/Win32/User32.cs @@ -1049,6 +1049,13 @@ public POINT(int x, int y) internal static class User32 { public const string LibraryName = "user32.dll"; + + /// + /// Does not activate the window, and does not discard the mouse message. + /// + public const int MA_NOACTIVATE = 3; + + public const int WM_MOUSEACTIVATE = 33; [DllImport(LibraryName, CharSet = CharSet.Unicode)] public static extern ushort RegisterClassEx([In] ref WNDCLASSEX lpwcx); @@ -1161,5 +1168,104 @@ public static extern IntPtr CreateWindowEx( [DllImport(LibraryName, ExactSpelling = true)] public static extern int GetSystemMetrics(int smIndex); + + private const int GWL_EXSTYLE = -20; + private const uint WS_EX_TOOLWINDOW = 0x00000080; + private const uint WS_EX_APPWINDOW = 0x00040000; + private const uint WS_EX_NOACTIVATE = 0x08000000; + private const uint WS_EX_TRANSPARENT = 0x00000020; + private const uint WS_EX_LAYERED = 0x00080000; + + /// + /// Sets whether the specified window allows mouse click-through (transparent to mouse events). + /// + /// The handle of the window. + /// True to make the window ignore mouse input; false to make it clickable. + public static void SetWindowClickThrough(IntPtr hwnd, bool clickThrough) + { + if (hwnd == IntPtr.Zero) + { + throw new ArgumentException("Invalid window handle."); + } + + uint exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + uint newStyle; + + if (clickThrough) + { + newStyle = exStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT; + } + else + { + newStyle = exStyle & ~WS_EX_TRANSPARENT; + } + + if (newStyle != exStyle) + { + SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); + } + } + + /// + /// Sets whether the specified window is visible in the taskbar. + /// + /// The handle of the window. + /// True to show in the taskbar, false to hide. + public static void SetTaskbarVisibility(IntPtr hwnd, bool visible) + { + if (hwnd == IntPtr.Zero) + { + throw new ArgumentException("Invalid window handle."); + } + + uint stylePtr = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + uint exStyle = stylePtr; + uint newStyle; + + if (visible) + { + newStyle = (exStyle & ~WS_EX_TOOLWINDOW) | WS_EX_APPWINDOW; + } + else + { + newStyle = (exStyle & ~WS_EX_APPWINDOW) | WS_EX_TOOLWINDOW; + } + + if (newStyle != exStyle) + { + SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); + } + } + + /// + /// Sets whether the specified window is allowed to activate (gain focus) when clicked. + /// + /// The handle of the window. + /// True to allow activation (default window behavior), false to suppress it. + public static void SetWindowActivationEnabled(IntPtr hwnd, bool allowActivation) + { + if (hwnd == IntPtr.Zero) + { + throw new ArgumentException("Invalid window handle."); + } + + uint stylePtr = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + uint exStyle = stylePtr; + uint newStyle; + + if (allowActivation) + { + newStyle = exStyle & ~WS_EX_NOACTIVATE; + } + else + { + newStyle = exStyle | WS_EX_NOACTIVATE; + } + + if (newStyle != exStyle) + { + SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); + } + } } -} +} \ No newline at end of file diff --git a/ClickableTransparentOverlay/Win32/Utils.cs b/ClickableTransparentOverlay/Win32/Utils.cs index b11f9ed..8035421 100644 --- a/ClickableTransparentOverlay/Win32/Utils.cs +++ b/ClickableTransparentOverlay/Win32/Utils.cs @@ -8,14 +8,6 @@ public static class Utils public static int Loword(int number) => number & 0x0000FFFF; public static int Hiword(int number) => number >> 16; - /// - /// Gets a value indicating whether the overlay is clickable or not. - /// - internal static bool IsClickable { get; private set; } = true; - - private static WindowExStyles Clickable = 0; - private static WindowExStyles NotClickable = 0; - private static readonly Stopwatch sw = Stopwatch.StartNew(); private static readonly long[] nVirtKeyTimeouts = new long[256]; // Total VirtKeys are 256. @@ -68,11 +60,8 @@ public static bool IsKeyPressedAndNotTimeout(VK nVirtKey, int timeout = 200) /// internal static void InitTransparency(IntPtr handle) { - Clickable = (WindowExStyles)User32.GetWindowLong(handle, (int)WindowLongParam.GWL_EXSTYLE); - NotClickable = Clickable | WindowExStyles.WS_EX_LAYERED | WindowExStyles.WS_EX_TRANSPARENT; var margins = new Dwmapi.Margins(-1); _ = Dwmapi.DwmExtendFrameIntoClientArea(handle, ref margins); - SetOverlayClickable(handle, true); } /// @@ -81,21 +70,31 @@ internal static void InitTransparency(IntPtr handle) /// /// Veldrid window handle in IntPtr format. /// Set to true if you want to make the window clickable otherwise false. - internal static void SetOverlayClickable(IntPtr handle, bool WantClickable) + internal static void SetOverlayClickable(IntPtr handle, bool WantClickable, ref bool IsClickable) { if (IsClickable ^ WantClickable) { - if (WantClickable) - { - User32.SetWindowLong(handle, (int)WindowLongParam.GWL_EXSTYLE, (uint)Clickable); - } - else - { - User32.SetWindowLong(handle, (int)WindowLongParam.GWL_EXSTYLE, (uint)NotClickable); - } - + User32.SetWindowClickThrough(handle, !WantClickable); IsClickable = WantClickable; } } + + internal static void SetShowInTaskbar(IntPtr handle, bool WantShowInTaskbar, ref bool ActualShowInTaskbar) + { + if (ActualShowInTaskbar ^ WantShowInTaskbar) + { + User32.SetTaskbarVisibility(handle, WantShowInTaskbar); + ActualShowInTaskbar = WantShowInTaskbar; + } + } + + internal static void SetNoActivate(IntPtr handle, bool WantNoActivate, ref bool ActualNoActivate) + { + if (ActualNoActivate ^ WantNoActivate) + { + User32.SetWindowActivationEnabled(handle, !WantNoActivate); + ActualNoActivate = WantNoActivate; + } + } } }