Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Created December 8, 2025 20:45
Show Gist options
  • Select an option

  • Save PlugFox/d72b23f86bf3e1e98d6222b56e0b683a to your computer and use it in GitHub Desktop.

Select an option

Save PlugFox/d72b23f86bf3e1e98d6222b56e0b683a to your computer and use it in GitHub Desktop.
Rust Windows Transparent Overlay
#[cfg(target_os = "windows")]
use windows::Win32::Foundation::{COLORREF, HINSTANCE, HWND, LPARAM, LRESULT, RECT, WPARAM};
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Dwm::DwmFlush;
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Gdi::HGDIOBJ;
#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Gdi::*;
#[cfg(target_os = "windows")]
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
#[cfg(target_os = "windows")]
use windows::Win32::UI::WindowsAndMessaging::*;
#[cfg(target_os = "windows")]
use windows::core::w;
use crate::status::Status;
pub struct Overlay {}
impl Default for Overlay {
fn default() -> Self {
Self {}
}
}
impl Overlay {
pub fn new() -> Self {
Self {}
}
pub async fn start<F>(&self, status: &Status, shutdown_signal: F)
where
F: std::future::Future<Output = ()> + Send + 'static,
{
// Pin the shutdown signal
tokio::pin!(shutdown_signal);
// Show the overlay
let h_module = unsafe { GetModuleHandleW(None).unwrap_or_default() };
let h_instance = HINSTANCE(h_module.0);
// Register window class
let class_name = w!("Keypad_Overlay");
let wc = WNDCLASSW {
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(draw_overlay),
hInstance: h_instance,
hCursor: unsafe { LoadCursorW(None, IDC_ARROW).unwrap_or_default() },
lpszClassName: class_name,
..Default::default()
};
let atom = unsafe { RegisterClassW(&wc) };
if atom == 0 {
return;
}
// Create layered, topmost, transparent, click-through window
let ex_style = WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_TOOLWINDOW; // toolwindow avoids taskbar
let style = WS_POPUP;
let hwnd = unsafe {
CreateWindowExW(
ex_style,
class_name,
w!("Keypad Overlay"),
style,
0,
0,
0,
0,
None,
None,
Some(h_instance),
None,
)
.unwrap()
};
// Make full-screen sized to current primary monitor
let width = unsafe { GetSystemMetrics(SM_CXSCREEN) };
let height = unsafe { GetSystemMetrics(SM_CYSCREEN) };
unsafe {
let _ = MoveWindow(hwnd, 0, 0, width, height, true);
};
unsafe {
let _ = ShowWindow(hwnd, SW_SHOW);
let _ = UpdateWindow(hwnd);
}
// Message loop with event-driven repaint
let mut msg = MSG::default();
let mut last_timestamp = status.timestamp;
loop {
while unsafe { PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() } {
if msg.message == WM_QUIT {
return;
}
unsafe {
let _ = TranslateMessage(&msg);
let _ = DispatchMessageW(&msg);
}
// Repaint only when state changes
let timestamp = status.timestamp;
if timestamp != last_timestamp {
let _ = unsafe { InvalidateRect(Some(hwnd), None, false) };
last_timestamp = timestamp;
}
// Block until the Desktop Window Manager has finished composition
let _ = unsafe { DwmFlush() };
}
}
}
}
#[cfg(target_os = "windows")]
unsafe extern "system" fn draw_overlay(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match msg {
WM_NCHITTEST => LRESULT(HTTRANSPARENT as isize),
WM_CREATE => {
// Use color key transparency: paint background black, make black transparent
let _ = unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY) };
LRESULT(0)
}
WM_ERASEBKGND => LRESULT(1), // prevent flicker
WM_PAINT => {
let mut ps = PAINTSTRUCT::default();
let hdc = unsafe { BeginPaint(hwnd, &mut ps) };
// Fill with black (the color key) to keep background transparent
let mut client = RECT::default();
let _ = unsafe { GetClientRect(hwnd, &mut client) };
let black_brush_obj = unsafe { GetStockObject(BLACK_BRUSH) };
let black_brush = HBRUSH(black_brush_obj.0);
unsafe { FillRect(hdc, &client, black_brush) };
// Compute box in top-right
let padding = 8;
let box_w = 64;
let box_h = 24;
let left = client.right - box_w - padding;
let top = client.top + padding;
let rect = RECT {
left,
top,
right: left + box_w,
bottom: top + box_h,
};
// Draw hollow rectangle (frame) with green/red depending on state
let on = true;
let color = if on { rgb(0, 255, 0) } else { rgb(255, 0, 0) };
let stock_null_brush = unsafe { GetStockObject(HOLLOW_BRUSH) };
let old_brush = unsafe { SelectObject(hdc, stock_null_brush) };
let pen = unsafe { CreatePen(PS_SOLID, 2, color) };
let old_pen = unsafe { SelectObject(hdc, HGDIOBJ(pen.0)) };
let _ = unsafe { Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom) };
// Text: On/Off centered
let text = if on { "On" } else { "Off" };
unsafe {
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, color);
}
let mut text_rect = rect;
let mut wide = to_wide(text);
unsafe {
DrawTextW(
hdc,
&mut wide,
&mut text_rect,
DT_CENTER | DT_SINGLELINE | DT_VCENTER,
)
};
// restore/select cleanup
unsafe {
SelectObject(hdc, old_pen);
SelectObject(hdc, old_brush);
let _ = DeleteObject(HGDIOBJ(pen.0));
let _ = EndPaint(hwnd, &ps);
}
LRESULT(0)
}
WM_DESTROY => {
unsafe { PostQuitMessage(0) };
LRESULT(0)
}
_ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
}
}
#[cfg(target_os = "windows")]
fn to_wide(s: &str) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;
std::ffi::OsStr::new(s)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
#[cfg(target_os = "windows")]
fn rgb(r: u8, g: u8, b: u8) -> COLORREF {
COLORREF((r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment