Set up Go module and move Rust to legacy
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
use super::WindowSnapshot;
|
||||
use crate::domain::EvidenceHealth;
|
||||
use regex::Regex;
|
||||
use std::{process::Command, process::Output, str};
|
||||
|
||||
pub fn get_title_clean() -> String {
|
||||
get_snapshot().title
|
||||
}
|
||||
|
||||
pub fn get_snapshot() -> WindowSnapshot {
|
||||
let window_info = get_window_info();
|
||||
let re = Regex::new(r"-?\d+([:.]\d+)+%?").unwrap();
|
||||
let title = re.replace_all(&window_info.title, "").to_string();
|
||||
let class = (window_info.class != "none").then_some(window_info.class);
|
||||
let health = if window_info.wid.is_empty() {
|
||||
EvidenceHealth::Unavailable("xdotool active window unavailable".to_string())
|
||||
} else {
|
||||
EvidenceHealth::Available
|
||||
};
|
||||
|
||||
WindowSnapshot {
|
||||
title,
|
||||
class,
|
||||
health,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn minimize_other(title: &str) {
|
||||
let window_info = get_window_info();
|
||||
if window_info.wid.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if window_info.title != title {
|
||||
run_xdotool(&["windowminimize", &window_info.wid]);
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowInfo {
|
||||
title: String,
|
||||
class: String,
|
||||
wid: String,
|
||||
}
|
||||
|
||||
fn is_valid_window_id(wid: &str) -> bool {
|
||||
!wid.is_empty() && wid.bytes().all(|byte| byte.is_ascii_digit())
|
||||
}
|
||||
|
||||
fn run_xdotool(args: &[&str]) -> Option<String> {
|
||||
let output = Command::new("xdotool").args(args).output();
|
||||
|
||||
let Ok(Output {
|
||||
status,
|
||||
stdout,
|
||||
stderr: _,
|
||||
}) = output
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if status.code() != Some(0) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Ok(output_str) = str::from_utf8(&stdout) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(output_str.trim().to_string())
|
||||
}
|
||||
|
||||
fn get_window_info() -> WindowInfo {
|
||||
let none = WindowInfo {
|
||||
title: "none".to_string(),
|
||||
class: "none".to_string(),
|
||||
wid: "".to_string(),
|
||||
};
|
||||
|
||||
let Some(wid) = run_xdotool(&["getactivewindow"]) else {
|
||||
return none;
|
||||
};
|
||||
|
||||
if !is_valid_window_id(&wid) {
|
||||
return none;
|
||||
};
|
||||
|
||||
let Some(class) = run_xdotool(&["getwindowclassname", &wid]) else {
|
||||
return none;
|
||||
};
|
||||
|
||||
let Some(title) = run_xdotool(&["getwindowname", &wid]) else {
|
||||
return none;
|
||||
};
|
||||
|
||||
WindowInfo { title, class, wid }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn valid_window_id_accepts_ascii_digits_only() {
|
||||
assert!(is_valid_window_id("12345"));
|
||||
assert!(is_valid_window_id("0"));
|
||||
|
||||
assert!(!is_valid_window_id(""));
|
||||
assert!(!is_valid_window_id("12abc"));
|
||||
assert!(!is_valid_window_id(" 123"));
|
||||
assert!(!is_valid_window_id("123 "));
|
||||
assert!(!is_valid_window_id("123"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use crate::domain::EvidenceHealth;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct WindowSnapshot {
|
||||
pub title: String,
|
||||
pub class: Option<String>,
|
||||
pub health: EvidenceHealth,
|
||||
}
|
||||
|
||||
impl WindowSnapshot {
|
||||
pub fn unavailable(reason: impl Into<String>) -> Self {
|
||||
Self {
|
||||
title: "none".to_string(),
|
||||
class: None,
|
||||
health: EvidenceHealth::Unavailable(reason.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use linux::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unavailable_snapshot_records_reason_without_evidence() {
|
||||
assert_eq!(
|
||||
WindowSnapshot::unavailable("no active window"),
|
||||
WindowSnapshot {
|
||||
title: "none".to_string(),
|
||||
class: None,
|
||||
health: EvidenceHealth::Unavailable("no active window".to_string()),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use super::WindowSnapshot;
|
||||
use crate::domain::EvidenceHealth;
|
||||
use regex::Regex;
|
||||
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
|
||||
use winapi::shared::windef::HWND;
|
||||
use winapi::um::winuser::{GetForegroundWindow, GetWindowTextW, ShowWindow, SW_MINIMIZE};
|
||||
|
||||
pub fn get_title_clean() -> String {
|
||||
get_snapshot().title
|
||||
}
|
||||
|
||||
pub fn get_snapshot() -> WindowSnapshot {
|
||||
let window_info = get_window_info();
|
||||
if window_info.hwnd.is_null() {
|
||||
return WindowSnapshot::unavailable("foreground window unavailable");
|
||||
}
|
||||
|
||||
let re = Regex::new(r"-?\d+([:.]\d+)+%?").unwrap();
|
||||
let title = re.replace_all(&window_info.title, "").to_string();
|
||||
let health = if window_info.title.is_empty() {
|
||||
EvidenceHealth::Degraded("foreground window title unavailable".to_string())
|
||||
} else {
|
||||
EvidenceHealth::Degraded("window class unavailable on current Windows adapter".to_string())
|
||||
};
|
||||
|
||||
WindowSnapshot {
|
||||
title,
|
||||
class: None,
|
||||
health,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn minimize_other(title: &str) {
|
||||
let window_info = get_window_info();
|
||||
if window_info.hwnd.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
if window_info.title != title {
|
||||
unsafe {
|
||||
ShowWindow(window_info.hwnd, SW_MINIMIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowInfo {
|
||||
title: String,
|
||||
hwnd: HWND,
|
||||
}
|
||||
|
||||
fn get_window_info() -> WindowInfo {
|
||||
unsafe {
|
||||
let hwnd = GetForegroundWindow();
|
||||
if hwnd.is_null() {
|
||||
return WindowInfo {
|
||||
title: String::new(),
|
||||
hwnd,
|
||||
};
|
||||
}
|
||||
|
||||
let mut text: [u16; 512] = [0; 512];
|
||||
let len = GetWindowTextW(hwnd, text.as_mut_ptr(), text.len() as i32) as usize;
|
||||
let title = OsString::from_wide(&text[..len])
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
WindowInfo { title, hwnd }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user