//! Persistenter Zustand unter `%APPDATA%\VitosUpdater` (Phase 1: Fensterposition und -größe). use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; /// Gespeicherte Fenstergeometrie in physischen Pixeln. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct WindowState { pub x: i32, pub y: i32, pub width: u32, pub height: u32, pub maximized: bool, } fn file(dir: &Path) -> PathBuf { dir.join("window.json") } /// Lädt die gespeicherte Fenstergeometrie. Fehlt die Datei oder ist sie defekt, ist das Ergebnis `None`. pub fn load_window_state(dir: &Path) -> Option { let text = std::fs::read_to_string(file(dir)).ok()?; serde_json::from_str(&text).ok() } /// Speichert die Fenstergeometrie atomar (Schreiben in temporäre Datei, dann Umbenennen). pub fn save_window_state(dir: &Path, state: &WindowState) -> std::io::Result<()> { std::fs::create_dir_all(dir)?; let tmp = dir.join("window.json.tmp"); std::fs::write(&tmp, serde_json::to_string_pretty(state)?)?; std::fs::rename(tmp, file(dir)) } /// Prüft, ob ein Fensterrechteck ein Monitorrechteck `(x, y, breite, höhe)` ausreichend überlappt. pub fn is_visible_on(state: &WindowState, monitors: &[(i32, i32, u32, u32)]) -> bool { const MIN_VISIBLE: i64 = 120; monitors.iter().any(|&(mx, my, mw, mh)| { let left = i64::from(state.x.max(mx)); let top = i64::from(state.y.max(my)); let right = (i64::from(state.x) + i64::from(state.width)).min(i64::from(mx) + i64::from(mw)); let bottom = (i64::from(state.y) + i64::from(state.height)).min(i64::from(my) + i64::from(mh)); right - left >= MIN_VISIBLE && bottom - top >= MIN_VISIBLE }) } #[cfg(test)] mod tests { use super::*; fn state(x: i32, y: i32) -> WindowState { WindowState { x, y, width: 1280, height: 800, maximized: false } } #[test] fn visible_on_primary_monitor() { assert!(is_visible_on(&state(100, 100), &[(0, 0, 1920, 1080)])); } #[test] fn offscreen_after_monitor_removed() { assert!(!is_visible_on(&state(2500, 100), &[(0, 0, 1920, 1080)])); } #[test] fn roundtrip() { let dir = std::env::temp_dir().join("vitosupdater-test-window"); let s = state(10, 20); save_window_state(&dir, &s).unwrap(); assert_eq!(load_window_state(&dir), Some(s)); let _ = std::fs::remove_dir_all(dir); } }