2026-03-27 17:19:33 +05:00

180 lines
4.9 KiB
Rust

use windows::core::PCWSTR;
use windows::Win32::Foundation::{HWND, RECT};
use windows::Win32::UI::Accessibility::{SetWinEventHook, UnhookWinEvent, HWINEVENTHOOK};
use windows::Win32::UI::Shell::{SHAppBarMessage, ABM_GETTASKBARPOS, APPBARDATA};
use windows::Win32::UI::WindowsAndMessaging::*;
// Window style constants
pub const WS_POPUP_STYLE: u32 = 0x80000000;
pub const WS_CHILD_STYLE: u32 = 0x40000000;
pub const WS_CLIPSIBLINGS_STYLE: u32 = 0x04000000;
// Win event constants
pub const EVENT_OBJECT_LOCATIONCHANGE: u32 = 0x800B;
pub const WINEVENT_OUTOFCONTEXT: u32 = 0x0000;
// Timer IDs
pub const TIMER_POLL: usize = 1;
pub const TIMER_COUNTDOWN: usize = 2;
pub const TIMER_RESET_POLL: usize = 3;
pub const TIMER_UPDATE_CHECK: usize = 4;
// Custom messages
pub const WM_APP: u32 = 0x8000;
pub const WM_APP_USAGE_UPDATED: u32 = WM_APP + 1;
pub const WM_APP_TRAY: u32 = WM_APP + 3;
/// Get the taskbar window handle
pub fn find_taskbar() -> Option<HWND> {
unsafe {
let class = wide_str("Shell_TrayWnd");
match FindWindowW(PCWSTR::from_raw(class.as_ptr()), PCWSTR::null()) {
Ok(h) if h != HWND::default() => Some(h),
_ => None,
}
}
}
/// Find a child window by class name
pub fn find_child_window(parent: HWND, class_name: &str) -> Option<HWND> {
unsafe {
let class = wide_str(class_name);
match FindWindowExW(
parent,
HWND::default(),
PCWSTR::from_raw(class.as_ptr()),
PCWSTR::null(),
) {
Ok(h) if h != HWND::default() => Some(h),
_ => None,
}
}
}
/// Get taskbar position via SHAppBarMessage
pub fn get_taskbar_rect(taskbar_hwnd: HWND) -> Option<RECT> {
unsafe {
let mut abd = APPBARDATA {
cbSize: std::mem::size_of::<APPBARDATA>() as u32,
hWnd: taskbar_hwnd,
..Default::default()
};
let result = SHAppBarMessage(ABM_GETTASKBARPOS, &mut abd);
if result == 0 {
return None;
}
Some(abd.rc)
}
}
/// Get the bounding rectangle of a window
pub fn get_window_rect_safe(hwnd: HWND) -> Option<RECT> {
unsafe {
let mut rect = RECT::default();
if GetWindowRect(hwnd, &mut rect).is_ok() {
Some(rect)
} else {
None
}
}
}
/// Embed our window as a child of the taskbar
pub fn embed_in_taskbar(hwnd: HWND, taskbar_hwnd: HWND) {
unsafe {
// Preserve existing extended style, add tool window + no activate
let ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
let _ = SetWindowLongW(
hwnd,
GWL_EXSTYLE,
ex_style | WS_EX_TOOLWINDOW.0 as i32 | WS_EX_NOACTIVATE.0 as i32,
);
// Change from popup to child
let style = GetWindowLongW(hwnd, GWL_STYLE) as u32;
let new_style = (style & !WS_POPUP_STYLE) | WS_CHILD_STYLE | WS_CLIPSIBLINGS_STYLE;
let _ = SetWindowLongW(hwnd, GWL_STYLE, new_style as i32);
let _ = SetParent(hwnd, taskbar_hwnd);
}
}
/// Move the window
pub fn move_window(hwnd: HWND, x: i32, y: i32, w: i32, h: i32) {
unsafe {
let _ = MoveWindow(hwnd, x, y, w, h, true);
}
}
/// Set up a WinEvent hook for tray location changes
pub fn set_tray_event_hook(
thread_id: u32,
callback: unsafe extern "system" fn(HWINEVENTHOOK, u32, HWND, i32, i32, u32, u32),
) -> Option<HWINEVENTHOOK> {
unsafe {
let hook = SetWinEventHook(
EVENT_OBJECT_LOCATIONCHANGE,
EVENT_OBJECT_LOCATIONCHANGE,
None,
Some(callback),
0,
thread_id,
WINEVENT_OUTOFCONTEXT,
);
if hook.is_invalid() {
None
} else {
Some(hook)
}
}
}
/// Get the thread ID that owns a window
pub fn get_window_thread_id(hwnd: HWND) -> u32 {
unsafe { GetWindowThreadProcessId(hwnd, None) }
}
/// Unhook a WinEvent hook
pub fn unhook_win_event(hook: HWINEVENTHOOK) {
unsafe {
let _ = UnhookWinEvent(hook);
}
}
/// Convert a Rust string to a null-terminated wide string
pub fn wide_str(s: &str) -> Vec<u16> {
s.encode_utf16().chain(std::iter::once(0)).collect()
}
/// COLORREF wrapper (RGB packed into u32)
pub fn colorref(r: u8, g: u8, b: u8) -> u32 {
r as u32 | (g as u32) << 8 | (b as u32) << 16
}
/// Color helper
#[derive(Clone, Copy, Debug)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Color {
#[allow(dead_code)]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
pub fn from_hex(hex: &str) -> Self {
let hex = hex.trim_start_matches('#');
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
Self { r, g, b }
}
pub fn to_colorref(self) -> u32 {
colorref(self.r, self.g, self.b)
}
}