use windows::{
core::PCWSTR,
Win32::{
Foundation::{HWND, RECT},
UI::WindowsAndMessaging::{
GetForegroundWindow, GetWindowLongW, GetWindowRect, SetForegroundWindow,
SetWindowLongW, SetWindowPos, SetWindowTextW, BringWindowToTop,
GWL_EXSTYLE, GWL_STYLE, HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOP, HWND_TOPMOST,
SWP_HIDEWINDOW, SWP_NOMOVE, SWP_NOSIZE, SWP_SHOWWINDOW,
},
},
};
use crate::utils::string_to_pcwstr;
pub fn show_window(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowPos(
hwnd,
None,
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW,
);
result.is_ok()
}
}
pub fn hide_window(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowPos(
hwnd,
None,
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW,
);
result.is_ok()
}
}
pub fn get_window_style(hwnd: HWND) -> Option<u32> {
if hwnd.0.is_null() {
return None;
}
unsafe {
let style = GetWindowLongW(hwnd, GWL_STYLE);
if style == 0 {
None
} else {
Some(style as u32)
}
}
}
pub fn get_window_ex_style(hwnd: HWND) -> Option<u32> {
if hwnd.0.is_null() {
return None;
}
unsafe {
let ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
if ex_style == 0 {
None
} else {
Some(ex_style as u32)
}
}
}
pub fn set_window_style(hwnd: HWND, style: u32) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowLongW(hwnd, GWL_STYLE, style as i32);
result != 0
}
}
pub fn set_window_ex_style(hwnd: HWND, ex_style: u32) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style as i32);
result != 0
}
}
pub fn add_window_style(hwnd: HWND, flags: u32) -> bool {
if let Some(current_style) = get_window_style(hwnd) {
let new_style = current_style | flags;
set_window_style(hwnd, new_style)
} else {
false
}
}
pub fn remove_window_style(hwnd: HWND, flags: u32) -> bool {
if let Some(current_style) = get_window_style(hwnd) {
let new_style = current_style & !flags;
set_window_style(hwnd, new_style)
} else {
false
}
}
pub fn add_window_ex_style(hwnd: HWND, flags: u32) -> bool {
if let Some(current_style) = get_window_ex_style(hwnd) {
let new_style = current_style | flags;
set_window_ex_style(hwnd, new_style)
} else {
false
}
}
pub fn remove_window_ex_style(hwnd: HWND, flags: u32) -> bool {
if let Some(current_style) = get_window_ex_style(hwnd) {
let new_style = current_style & !flags;
set_window_ex_style(hwnd, new_style)
} else {
false
}
}
pub fn bring_to_front(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowPos(hwnd, Some(HWND_TOP), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
result.is_ok()
}
}
pub fn send_to_back(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowPos(hwnd, Some(HWND_BOTTOM), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
result.is_ok()
}
}
pub fn set_always_on_top(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowPos(
hwnd,
Some(HWND_TOPMOST),
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE,
);
result.is_ok()
}
}
pub fn remove_always_on_top(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
let result = SetWindowPos(
hwnd,
Some(HWND_NOTOPMOST),
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE,
);
result.is_ok()
}
}
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
if hwnd.0.is_null() {
return None;
}
unsafe {
let mut rect = RECT::default();
match GetWindowRect(hwnd, &mut rect) {
Ok(_) => Some(rect),
Err(_) => None,
}
}
}
pub fn get_window_bounding_rect(hwnd: HWND) -> Option<(i32, i32, i32, i32)> {
get_window_rect(hwnd).map(|rect| {
let x = rect.left;
let y = rect.top;
let width = rect.right - rect.left;
let height = rect.bottom - rect.top;
(x, y, width, height)
})
}
pub fn set_window_title(hwnd: HWND, title: &str) -> bool {
if hwnd.0.is_null() {
return false;
}
let utf16_title = string_to_pcwstr(title);
unsafe {
let result = SetWindowTextW(hwnd, PCWSTR::from_raw(utf16_title.as_ptr()));
result.is_ok()
}
}
#[inline]
pub fn is_foreground_window(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe { GetForegroundWindow() == hwnd }
}
pub fn activate_window(hwnd: HWND, timeout_ms: u64) -> Result<(), WindowActivationError> {
if hwnd.0.is_null() {
return Err(WindowActivationError::InvalidHandle);
}
if is_foreground_window(hwnd) {
return Ok(());
}
unsafe {
let primary = SetForegroundWindow(hwnd).as_bool();
if !primary && BringWindowToTop(hwnd).is_err() {
return Err(WindowActivationError::ActivationFailed);
}
}
let mut delay_ms = 1u64;
let start = std::time::Instant::now();
let timeout = std::time::Duration::from_millis(timeout_ms);
while start.elapsed() < timeout {
if is_foreground_window(hwnd) {
return Ok(());
}
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
delay_ms = (delay_ms * 2).min(50);
}
Err(WindowActivationError::Timeout)
}
#[inline]
pub fn activate_window_quick(hwnd: HWND) -> bool {
activate_window(hwnd, 200).is_ok()
}
#[inline]
pub fn ensure_window_active(hwnd: HWND, timeout_ms: u64) -> Result<(), WindowActivationError> {
activate_window(hwnd, timeout_ms)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WindowActivationError {
InvalidHandle,
ActivationFailed,
Timeout,
}
impl std::fmt::Display for WindowActivationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidHandle => write!(f, "Invalid window handle"),
Self::ActivationFailed => write!(f, "Failed to activate window"),
Self::Timeout => write!(f, "Window activation timed out"),
}
}
}
impl std::error::Error for WindowActivationError {}
pub fn hide_window_completely(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
use windows::Win32::UI::WindowsAndMessaging::{ShowWindow, SW_HIDE, SW_SHOW};
let _ = ShowWindow(hwnd, SW_HIDE);
if let Some(mut ex_style) = get_window_ex_style(hwnd) {
ex_style &= !0x00040000;
ex_style |= 0x00000080;
set_window_ex_style(hwnd, ex_style);
}
let _ = ShowWindow(hwnd, SW_SHOW);
let result = ShowWindow(hwnd, SW_HIDE);
result.as_bool()
}
}
pub fn show_window_completely(hwnd: HWND) -> bool {
if hwnd.0.is_null() {
return false;
}
unsafe {
use windows::Win32::UI::WindowsAndMessaging::{ShowWindow, SW_RESTORE};
if let Some(mut ex_style) = get_window_ex_style(hwnd) {
ex_style &= !0x00000080;
ex_style |= 0x00040000;
set_window_ex_style(hwnd, ex_style);
}
let _ = ShowWindow(hwnd, SW_RESTORE);
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hwnd::get_hwnd_list;
#[test]
fn test_get_window_rect_invalid() {
let result = get_window_rect(HWND::default());
assert!(result.is_none());
}
#[test]
fn test_get_window_bounding_rect() {
let hwnds = get_hwnd_list();
if let Some(&hwnd) = hwnds.first() {
if let Some((x, y, w, h)) = get_window_bounding_rect(hwnd) {
println!("\nWindow bounding rect:");
println!(" Position: ({}, {})", x, y);
println!(" Size: {}x{}", w, h);
assert!(w >= 0, "Width should be non-negative");
assert!(h >= 0, "Height should be non-negative");
if let Some(rect) = get_window_rect(hwnd) {
assert_eq!(x, rect.left, "X should match rect.left");
assert_eq!(y, rect.top, "Y should match rect.top");
assert_eq!(
w,
rect.right - rect.left,
"Width should match rect.right - rect.left"
);
assert_eq!(
h,
rect.bottom - rect.top,
"Height should match rect.bottom - rect.top"
);
}
}
let invalid_result = get_window_bounding_rect(HWND::default());
assert!(
invalid_result.is_none(),
"Should return None for invalid HWND"
);
}
}
#[test]
fn test_window_style_operations() {
let hwnds = get_hwnd_list();
if let Some(&hwnd) = hwnds.first() {
if let Some(style) = get_window_style(hwnd) {
println!("Original style: 0x{:X}", style);
if style & 0x00C00000 != 0 {
println!(" - Has caption/title bar (WS_CAPTION)");
}
if style & 0x00080000 != 0 {
println!(" - Has system menu (WS_SYSMENU)");
}
if style & 0x00040000 != 0 {
println!(" - Has thick frame/resizable (WS_THICKFRAME)");
}
if style & 0x10000000 != 0 {
println!(" - Is visible (WS_VISIBLE)");
}
if style & 0x00020000 != 0 {
println!(" - Has minimize box (WS_MINIMIZEBOX)");
}
if style & 0x00010000 != 0 {
println!(" - Has maximize box (WS_MAXIMIZEBOX)");
}
let _ = add_window_style(hwnd, 0x00000001);
let _ = remove_window_style(hwnd, 0x00000001);
}
if let Some(ex_style) = get_window_ex_style(hwnd) {
println!("\nOriginal ex_style: 0x{:X}", ex_style);
if ex_style & 0x00000008 != 0 {
println!(" - Is topmost (WS_EX_TOPMOST)");
}
if ex_style & 0x00000080 != 0 {
println!(" - Is tool window (WS_EX_TOOLWINDOW)");
}
if ex_style & 0x00080000 != 0 {
println!(" - Is layered/transparency (WS_EX_LAYERED)");
}
if ex_style & 0x00000100 != 0 {
println!(" - Has raised border (WS_EX_WINDOWEDGE)");
}
if ex_style & 0x00000200 != 0 {
println!(" - Has sunken border (WS_EX_CLIENTEDGE)");
}
if ex_style & 0x00200000 != 0 {
println!(" - Is composited/double-buffered (WS_EX_COMPOSITED)");
}
let _ = add_window_ex_style(hwnd, 0x00000001);
let _ = remove_window_ex_style(hwnd, 0x00000001);
}
}
}
#[test]
fn test_z_order_operations() {
let hwnds = get_hwnd_list();
if let Some(&hwnd) = hwnds.first() {
let _ = bring_to_front(hwnd);
let _ = send_to_back(hwnd);
let _ = set_always_on_top(hwnd);
let _ = remove_always_on_top(hwnd);
}
}
#[test]
fn test_show_hide_window() {
let hwnds = get_hwnd_list();
if let Some(&hwnd) = hwnds.first() {
let _ = show_window(hwnd);
}
}
#[test]
fn test_hide_show_window_completely() {
let hwnds = get_hwnd_list();
if let Some(&hwnd) = hwnds.iter().find(|&&h| {
let title = crate::hwnd::get_window_title_for_test(h);
!title.is_empty() && title != "Program Manager"
}) {
println!("\nTesting complete hide/show on window: {:?}", hwnd);
if let Some(initial_ex_style) = get_window_ex_style(hwnd) {
println!("Initial ex_style: 0x{:08X}", initial_ex_style);
let has_toolwindow = initial_ex_style & 0x00000080 != 0;
let has_appwindow = initial_ex_style & 0x00040000 != 0;
println!(" WS_EX_TOOLWINDOW: {}", has_toolwindow);
println!(" WS_EX_APPWINDOW: {}", has_appwindow);
}
let hide_result = hide_window_completely(hwnd);
println!("hide_window_completely result: {}", hide_result);
std::thread::sleep(std::time::Duration::from_millis(100));
if let Some(hidden_ex_style) = get_window_ex_style(hwnd) {
println!("After hide ex_style: 0x{:08X}", hidden_ex_style);
let has_toolwindow = hidden_ex_style & 0x00000080 != 0;
let has_appwindow = hidden_ex_style & 0x00040000 != 0;
println!(" WS_EX_TOOLWINDOW: {} (expected: true)", has_toolwindow);
println!(" WS_EX_APPWINDOW: {} (expected: false)", has_appwindow);
if !has_toolwindow {
eprintln!("Warning: Window may still be visible (no WS_EX_TOOLWINDOW)");
}
if has_appwindow {
eprintln!("Warning: Window may still appear in taskbar (has WS_EX_APPWINDOW)");
}
}
let show_result = show_window_completely(hwnd);
println!("show_window_completely result: {}", show_result);
std::thread::sleep(std::time::Duration::from_millis(100));
if let Some(shown_ex_style) = get_window_ex_style(hwnd) {
println!("After show ex_style: 0x{:08X}", shown_ex_style);
let has_toolwindow = shown_ex_style & 0x00000080 != 0;
let has_appwindow = shown_ex_style & 0x00040000 != 0;
println!(" WS_EX_TOOLWINDOW: {} (expected: false)", has_toolwindow);
println!(" WS_EX_APPWINDOW: {} (expected: true)", has_appwindow);
if has_toolwindow {
eprintln!("Warning: Window may still be hidden (has WS_EX_TOOLWINDOW)");
}
if !has_appwindow {
eprintln!("Warning: Window may not appear in taskbar (no WS_EX_APPWINDOW)");
}
}
println!("Complete hide/show test finished (non-assertive mode)");
} else {
println!("No suitable window found for complete hide/show test");
}
}
#[test]
fn test_style_flag_decoding_example() {
println!("\n=== Window Style Flag Decoding Example ===\n");
let hwnds = get_hwnd_list();
if let Some(&hwnd) = hwnds.iter().take(3).last() {
println!("Analyzing window: {:?}", hwnd);
if let Some(style) = get_window_style(hwnd) {
println!("\nWindow Style (GWL_STYLE): 0x{:08X}", style);
println!("Binary: {:032b}", style);
println!("\n[Window Type]");
if style & 0x80000000 != 0 && style & 0x40000000 == 0 {
println!(" ✓ WS_OVERLAPPED/POPUP (0x80000000)");
} else if style & 0x40000000 != 0 {
println!(" ✓ WS_CHILD (0x40000000)");
}
println!("\n[Appearance]");
if style & 0x00C00000 != 0 {
println!(" ✓ WS_CAPTION (0x00C00000)");
}
if style & 0x00800000 != 0 {
println!(" ✓ WS_BORDER (0x00800000)");
}
if style & 0x00040000 != 0 {
println!(" ✓ WS_THICKFRAME (0x00040000)");
}
println!("\n[System Features]");
if style & 0x00080000 != 0 {
println!(" ✓ WS_SYSMENU (0x00080000)");
}
if style & 0x00020000 != 0 {
println!(" ✓ WS_MINIMIZEBOX (0x00020000)");
}
if style & 0x00010000 != 0 {
println!(" ✓ WS_MAXIMIZEBOX (0x00010000)");
}
println!("\n[State]");
if style & 0x10000000 != 0 {
println!(" ✓ WS_VISIBLE (0x10000000)");
}
if style & 0x20000000 != 0 {
println!(" ✓ WS_DISABLED (0x20000000)");
}
}
if let Some(ex_style) = get_window_ex_style(hwnd) {
println!("\nExtended Style (GWL_EXSTYLE): 0x{:08X}", ex_style);
println!("Binary: {:032b}", ex_style);
println!("\n[Extended Features]");
if ex_style & 0x00000008 != 0 {
println!(" ✓ WS_EX_TOPMOST (0x00000008)");
}
if ex_style & 0x00000080 != 0 {
println!(" ✓ WS_EX_TOOLWINDOW (0x00000080)");
}
if ex_style & 0x00080000 != 0 {
println!(" ✓ WS_EX_LAYERED (0x00080000)");
}
if ex_style & 0x00040000 != 0 {
println!(" ✓ WS_EX_NOACTIVATE (0x00040000)");
}
if ex_style & 0x00200000 != 0 {
println!(" ✓ WS_EX_COMPOSITED (0x00200000)");
}
if ex_style & 0x00010000 != 0 {
println!(" ✓ WS_EX_APPWINDOW (0x00010000)");
}
}
}
}
}