first commit
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
use clap::Parser;
|
||||
use winsafe::{
|
||||
co::{PROCESS, PROCESS_NAME, SWP},
|
||||
prelude::*,
|
||||
EnumWindows, HwndPlace, HPROCESS, HWND, POINT,
|
||||
};
|
||||
|
||||
mod model;
|
||||
use model::*;
|
||||
|
||||
fn filter_target_windows(hwnd: &HWND, q: &TargetInformation) -> bool {
|
||||
if !q.title_contains.is_empty() {
|
||||
let Ok(title) = hwnd.GetWindowText() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if q.title_contains.iter().all(|s| !title.contains(s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !q.path_endswith.is_empty() {
|
||||
let (_, pid) = hwnd.GetWindowThreadProcessId();
|
||||
|
||||
let Ok(proc) = HPROCESS::OpenProcess(PROCESS::QUERY_LIMITED_INFORMATION, false, pid) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Ok(path) = proc.QueryFullProcessImageName(PROCESS_NAME::WIN32) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if q.path_endswith.iter().all(|s| !path.ends_with(s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn window_callback(hwnd: HWND, op: &Commands) {
|
||||
let t = match op {
|
||||
Commands::Get { target } => target,
|
||||
Commands::Set {
|
||||
target,
|
||||
resolution: _,
|
||||
} => target,
|
||||
};
|
||||
|
||||
if !filter_target_windows(&hwnd, t) {
|
||||
return;
|
||||
}
|
||||
|
||||
let client_size = Size::from(hwnd.GetClientRect().unwrap());
|
||||
|
||||
match op {
|
||||
Commands::Get { target: _ } => {
|
||||
println!("{}", client_size - t.offset);
|
||||
}
|
||||
Commands::Set {
|
||||
target: _,
|
||||
resolution,
|
||||
} => {
|
||||
let window_size = Size::from(hwnd.GetWindowRect().unwrap());
|
||||
let border = window_size - client_size;
|
||||
|
||||
hwnd.SetWindowPos(
|
||||
HwndPlace::None,
|
||||
POINT::new(0, 0),
|
||||
(*resolution + t.offset + border).into(),
|
||||
SWP::NOMOVE,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let c = Cli::parse().command;
|
||||
EnumWindows(|hwnd: HWND| -> bool {
|
||||
window_callback(hwnd, &c);
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use std::fmt::{self, Display};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::ops::{Add, Sub};
|
||||
use winsafe::{RECT, SIZE};
|
||||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct TargetInformation {
|
||||
#[arg(short, long)]
|
||||
pub path_endswith: Vec<String>,
|
||||
#[arg(short, long)]
|
||||
pub title_contains: Vec<String>,
|
||||
#[arg(short, long, value_parser = parse_size, default_value_t = Size::default())]
|
||||
pub offset: Size,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
pub enum Commands {
|
||||
Get {
|
||||
#[command(flatten)]
|
||||
target: TargetInformation,
|
||||
},
|
||||
Set {
|
||||
#[command(flatten)]
|
||||
target: TargetInformation,
|
||||
#[arg(value_parser = parse_size)]
|
||||
resolution: Size,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Parser)]
|
||||
pub struct Size {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
}
|
||||
|
||||
impl Add for Size {
|
||||
type Output = Size;
|
||||
fn add(self, other: Self) -> Self {
|
||||
Self {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Size {
|
||||
type Output = Size;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Self {
|
||||
x: self.x - other.x,
|
||||
y: self.y - other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RECT> for Size {
|
||||
fn from(v: RECT) -> Self {
|
||||
Self {
|
||||
x: (v.right - v.left) as usize,
|
||||
y: (v.bottom - v.top) as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Size> for SIZE {
|
||||
fn from(v: Size) -> Self {
|
||||
Self {
|
||||
cx: v.x as i32,
|
||||
cy: v.y as i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Size {
|
||||
fn default() -> Self {
|
||||
Self { x: 0, y: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Size {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}x{}", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_size(arg: &str) -> Result<Size, Error> {
|
||||
let mut res = arg.split('x');
|
||||
|
||||
let Some(x) = res.next() else {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "Unexpected Input"));
|
||||
};
|
||||
|
||||
let Some(y) = res.next() else {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "Unexpected Input"));
|
||||
};
|
||||
|
||||
let None = res.next() else {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "Unexpected Input"));
|
||||
};
|
||||
|
||||
Ok(Size {
|
||||
x: x.parse()
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidInput, "Unexpected Input"))?,
|
||||
y: y.parse()
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidInput, "Unexpected Input"))?,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user