Skip to content

Instantly share code, notes, and snippets.

@pjmagee
Created November 15, 2025 17:53
Show Gist options
  • Select an option

  • Save pjmagee/1af86931ce99c5260ce4a968380371af to your computer and use it in GitHub Desktop.

Select an option

Save pjmagee/1af86931ce99c5260ce4a968380371af to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
namespace TransparentOverlay
{
public partial class MainWindow : Window
{
private IntPtr _notepadHwnd = IntPtr.Zero;
private DispatcherTimer _clockTimer;
private Timer _trackTimer;
private const int TrackIntervalMs = 5;
private RECT _lastRect;
private bool _initializedStyles;
public MainWindow()
{
InitializeComponent();
// Enable hardware acceleration
RenderOptions.ProcessRenderMode = RenderMode.Default;
Loaded += OnLoaded;
}
private void OnLoaded(object? sender, RoutedEventArgs e)
{
EnsureNotepadWindow();
SetupClock();
SetupTrackingTimer();
// Initial positioning attempt
if (_notepadHwnd != IntPtr.Zero && GetWindowRect(_notepadHwnd, out var r))
{
UpdateOverlayBounds(r);
}
else
{
// Fallback visible size so clock is seen until Notepad is found
Width = 200;
Height = 80;
Left = (SystemParameters.PrimaryScreenWidth - Width) / 2;
Top = 40;
}
ApplyTransparentClickThrough();
}
private void SetupClock()
{
// Update at ~60fps for smooth millisecond rendering
_clockTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) };
_clockTimer.Tick += (_, __) => ClockText.Text = DateTime.Now.ToString("HH:mm:ss.fff");
ClockText.Text = DateTime.Now.ToString("HH:mm:ss.fff");
_clockTimer.Start();
}
private void SetupTrackingTimer()
{
_trackTimer = new Timer(_ => TrackNotepad(), null, TrackIntervalMs, TrackIntervalMs);
}
private void EnsureNotepadWindow()
{
var processes = Process.GetProcessesByName("notepad");
if (processes.Length == 0)
{
try
{
Process.Start(new ProcessStartInfo("notepad.exe") { UseShellExecute = true });
Thread.Sleep(300);
processes = Process.GetProcessesByName("notepad");
}
catch { }
}
foreach (var proc in processes)
{
if (proc.MainWindowHandle != IntPtr.Zero)
{
_notepadHwnd = proc.MainWindowHandle;
break;
}
}
}
private void TrackNotepad()
{
if (_notepadHwnd == IntPtr.Zero)
{
EnsureNotepadWindow();
if (_notepadHwnd == IntPtr.Zero) return;
}
if (!GetWindowRect(_notepadHwnd, out RECT rect)) return;
if (rect.Equals(_lastRect)) return;
_lastRect = rect;
// Use Render priority for smoother tracking during rapid moves
Dispatcher.BeginInvoke(() => UpdateOverlayBounds(rect), DispatcherPriority.Send);
}
private void UpdateOverlayBounds(RECT rect)
{
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
// Update WPF properties
Left = rect.Left;
Top = rect.Top;
Width = width;
Height = height;
var hwnd = new WindowInteropHelper(this).Handle;
if (hwnd != IntPtr.Zero)
{
// Use SWP_ASYNCWINDOWPOS for async positioning to reduce lag
SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, width, height,
SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS);
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Enable hardware rendering on composition target
var hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource?.CompositionTarget != null)
{
hwndSource.CompositionTarget.RenderMode = RenderMode.Default;
}
ApplyTransparentClickThrough();
}
private void ApplyTransparentClickThrough()
{
if (_initializedStyles) return;
var hwnd = new WindowInteropHelper(this).Handle;
if (hwnd == IntPtr.Zero) return;
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
exStyle |= WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW;
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle);
_initializedStyles = true;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_trackTimer?.Dispose();
_clockTimer?.Stop();
}
#region Win32
private const int GWL_EXSTYLE = -20;
private const int WS_EX_TRANSPARENT = 0x00000020;
private const int WS_EX_TOOLWINDOW = 0x00000080;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const uint SWP_NOACTIVATE = 0x0010;
private const uint SWP_SHOWWINDOW = 0x0040;
private const uint SWP_ASYNCWINDOWPOS = 0x4000;
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[StructLayout(LayoutKind.Sequential)]
private struct RECT : IEquatable<RECT>
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public bool Equals(RECT other) => Left == other.Left && Top == other.Top && Right == other.Right && Bottom == other.Bottom;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment