use std::thread;
use std::time::Duration;
use clap::{arg, ArgMatches, Command};
use rand::{rng, RngExt};
use windows::Win32::Foundation::HWND;
use windows_troll::modules::window_hider;
use crate::cli::CliModule;
pub struct WindowHiderCli;
impl CliModule for WindowHiderCli {
fn name(&self) -> &'static str {
"window-hider"
}
fn about(&self) -> &'static str {
"Minimize (hide) and restore windows"
}
fn command(&self) -> Command {
Command::new(self.name())
.about(self.about())
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
Command::new("list").about("List visible windows with their titles and handles"),
)
.subcommand(
Command::new("hide")
.about("Minimize a window")
.arg(arg!(-t --title <TITLE> "Minimize the first window with this exact title")),
)
.subcommand(
Command::new("restore")
.about("Restore a minimized window by handle")
.arg(arg!(<HWND> "Window handle (decimal, as shown by `list`)").value_parser(clap::value_parser!(isize))),
)
.subcommand(
Command::new("loop")
.about("Continuously minimize a random window")
.arg(
arg!(-m --min <SECONDS> "Minimum wait time in seconds")
.default_value("10")
.value_parser(clap::value_parser!(u64)),
)
.arg(
arg!(-M --max <SECONDS> "Maximum wait time in seconds")
.default_value("60")
.value_parser(clap::value_parser!(u64)),
),
)
}
fn run(&self, matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
match matches.subcommand() {
Some(("list", _)) => run_list(),
Some(("hide", m)) => run_hide(m),
Some(("restore", m)) => run_restore(m),
Some(("loop", m)) => run_loop(m),
_ => unreachable!("exhausted subcommands"),
}
}
}
fn run_list() -> Result<(), Box<dyn std::error::Error>> {
let windows = window_hider::list_windows();
if windows.is_empty() {
println!("No visible windows with a title found");
return Ok(());
}
println!("{:>4} {:>12} title", "idx", "handle");
for (i, w) in windows.iter().enumerate() {
println!("{:>4} {:>12} {}", i, w.handle().0 as isize, w.title());
}
Ok(())
}
fn run_hide(m: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
match m.get_one::<String>("title") {
Some(title) => match window_hider::hide_by_title(title) {
Some(w) => println!("Minimized: \"{}\"", w.title()),
None => println!("No window with title \"{}\" found", title),
},
None => match window_hider::hide_random_window() {
Some(w) => println!("Minimized: \"{}\"", w.title()),
None => println!("No eligible windows found"),
},
}
Ok(())
}
fn run_restore(m: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let raw = *m.get_one::<isize>("HWND").expect("required argument");
let hwnd = HWND(raw as *mut _);
if window_hider::restore_window(hwnd) {
println!("Restored window handle {raw}");
} else {
println!("Window handle {raw}: nothing to restore (already visible)");
}
Ok(())
}
fn run_loop(m: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let mut min = *m.get_one::<u64>("min").expect("has default");
let mut max = *m.get_one::<u64>("max").expect("has default");
if min > max {
std::mem::swap(&mut min, &mut max);
}
println!("window-hider loop started. Press Ctrl+C to exit.");
println!("wait range: {min}..={max} seconds");
let mut rng = rng();
loop {
let wait = rng.random_range(min..=max);
println!("waiting {wait}s...");
thread::sleep(Duration::from_secs(wait));
match window_hider::hide_random_window() {
Some(w) => println!("minimized: \"{}\"", w.title()),
None => println!("no eligible windows found"),
}
}
}