//! VitosUpdater – einheitliche Paketverwaltung für Windows. pub mod commands; pub mod error; pub mod logging; pub mod managers; pub mod models; pub mod process; pub mod settings; pub mod updater; use std::path::PathBuf; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; use tauri::{Manager, PhysicalPosition, PhysicalSize, WebviewWindow, WindowEvent}; use settings::WindowState; /// Letzte normale (nicht maximierte) Geometrie und Zähler für das Entprellen des Speicherns. struct WindowTracker { dir: PathBuf, normal: Mutex>, generation: AtomicU64, } fn restore_window(window: &WebviewWindow, saved: Option) { if let Some(state) = saved { let monitors: Vec<(i32, i32, u32, u32)> = window .available_monitors() .unwrap_or_default() .iter() .map(|m| (m.position().x, m.position().y, m.size().width, m.size().height)) .collect(); if settings::is_visible_on(&state, &monitors) { let _ = window.set_size(PhysicalSize::new(state.width, state.height)); let _ = window.set_position(PhysicalPosition::new(state.x, state.y)); } if state.maximized { let _ = window.maximize(); } } let _ = window.show(); } fn track_window(window: &WebviewWindow, tracker: &Arc) { let maximized = window.is_maximized().unwrap_or(false); let minimized = window.is_minimized().unwrap_or(false); if minimized { return; } { let mut normal = tracker.normal.lock().unwrap_or_else(|e| e.into_inner()); if !maximized { if let (Ok(pos), Ok(size)) = (window.outer_position(), window.inner_size()) { *normal = Some(WindowState { x: pos.x, y: pos.y, width: size.width, height: size.height, maximized: false }); } } else if let Some(state) = normal.as_mut() { state.maximized = true; } } let generation = tracker.generation.fetch_add(1, Ordering::SeqCst) + 1; let tracker = Arc::clone(tracker); tauri::async_runtime::spawn(async move { tokio::time::sleep(Duration::from_millis(500)).await; if tracker.generation.load(Ordering::SeqCst) != generation { return; } let snapshot = *tracker.normal.lock().unwrap_or_else(|e| e.into_inner()); if let Some(mut state) = snapshot { state.maximized = maximized; if let Err(err) = settings::save_window_state(&tracker.dir, &state) { tracing::warn!(%err, "Fensterzustand konnte nicht gespeichert werden"); } } }); } /// Startet die Anwendung. pub fn run() { let dir = logging::data_dir(); let _guard = logging::init(&dir); tracing::info!(version = env!("CARGO_PKG_VERSION"), "VitosUpdater startet"); let tracker = Arc::new(WindowTracker { dir: dir.clone(), normal: Mutex::new(settings::load_window_state(&dir)), generation: AtomicU64::new(0), }); let saved = settings::load_window_state(&dir); let event_tracker = Arc::clone(&tracker); let result = tauri::Builder::default() .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { if let Some(window) = app.get_webview_window("main") { let _ = window.unminimize(); let _ = window.show(); let _ = window.set_focus(); } })) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_updater::Builder::new().build()) .manage(commands::Registry::new()) .manage(updater::UpdaterState::new(dir.clone())) .invoke_handler(tauri::generate_handler![ commands::detect_managers, commands::search_packages, commands::list_installed, commands::list_updates, commands::get_package_details, commands::list_sources, commands::install_winget_module, commands::install_app_installer, updater::updater_ready, updater::get_updater_status, updater::get_update_offer, updater::check_for_update, updater::snooze_update, updater::skip_update, updater::clear_skipped_version, updater::set_update_interval, updater::install_update, updater::take_update_notice, ]) .setup(move |app| { if let Some(window) = app.get_webview_window("main") { restore_window(&window, saved); } Ok(()) }) .on_window_event(move |window, event| { if matches!(event, WindowEvent::Moved(_) | WindowEvent::Resized(_)) { if let Some(webview) = window.app_handle().get_webview_window(window.label()) { track_window(&webview, &event_tracker); } } }) .run(tauri::generate_context!()); if let Err(err) = result { tracing::error!(%err, "Anwendung wurde mit einem Fehler beendet"); std::process::exit(1); } }