diff --git a/src/core/database.rs b/src/core/database.rs index 8083f0e..c9c3ddf 100644 --- a/src/core/database.rs +++ b/src/core/database.rs @@ -1744,6 +1744,16 @@ impl Database { Ok(self.conn.last_insert_rowid()) } + // --- Autostart --- + + pub fn set_autostart(&self, id: i64, enabled: bool) -> SqlResult<()> { + self.conn.execute( + "UPDATE appimages SET autostart = ?2 WHERE id = ?1", + params![id, enabled as i32], + )?; + Ok(()) + } + // --- Launch statistics --- pub fn get_top_launched(&self, limit: i32) -> SqlResult> { diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 44d7d1a..311d929 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -245,6 +245,61 @@ pub fn integrate_tracked(record: &AppImageRecord, db: &Database) -> Result PathBuf { + dirs::config_dir() + .unwrap_or_else(|| PathBuf::from("/tmp")) + .join("autostart") +} + +pub fn enable_autostart(db: &Database, record: &AppImageRecord) -> Result { + let dir = autostart_dir(); + fs::create_dir_all(&dir).map_err(|e| format!("Failed to create autostart dir: {}", e))?; + + let app_id = record.app_name.as_deref() + .map(|n| make_app_id(n)) + .unwrap_or_else(|| format!("appimage-{}", record.id)); + let desktop_filename = format!("driftwood-{}.desktop", app_id); + let desktop_path = dir.join(&desktop_filename); + + let app_name = record.app_name.as_deref().unwrap_or(&record.filename); + let icon = record.icon_path.as_deref().unwrap_or("application-x-executable"); + + let content = format!("\ +[Desktop Entry] +Type=Application +Name={} +Exec=\"{}\" %U +Icon={} +X-GNOME-Autostart-enabled=true +X-Driftwood-AppImage-ID={} +", app_name, escape_exec_arg(&record.path), icon, record.id); + + fs::write(&desktop_path, &content) + .map_err(|e| format!("Failed to write autostart file: {}", e))?; + + db.register_modification(record.id, "autostart", &desktop_path.to_string_lossy(), None) + .map_err(|e| format!("Failed to register modification: {}", e))?; + + db.set_autostart(record.id, true).ok(); + + Ok(desktop_path) +} + +pub fn disable_autostart(db: &Database, record_id: i64) -> Result<(), String> { + let mods = db.get_modifications(record_id).unwrap_or_default(); + for m in &mods { + if m.mod_type == "autostart" { + let path = Path::new(&m.file_path); + if path.exists() { + fs::remove_file(path).ok(); + } + db.remove_modification(m.id).ok(); + } + } + db.set_autostart(record_id, false).ok(); + Ok(()) +} + /// Undo all tracked system modifications for an AppImage. pub fn undo_all_modifications(db: &Database, appimage_id: i64) -> Result<(), String> { let mods = db.get_modifications(appimage_id) diff --git a/src/ui/detail_view.rs b/src/ui/detail_view.rs index d4e3738..2fe2971 100644 --- a/src/ui/detail_view.rs +++ b/src/ui/detail_view.rs @@ -990,6 +990,39 @@ fn build_system_tab(record: &AppImageRecord, db: &Rc, toast_overlay: & integration_group.add(&row); } } + // Autostart toggle + let autostart_row = adw::SwitchRow::builder() + .title("Start at login") + .subtitle("Launch this app automatically when you log in") + .active(record.autostart) + .tooltip_text( + "Creates an autostart entry so this app launches \ + when you log in to your desktop." + ) + .build(); + let record_autostart = record.clone(); + let db_autostart = db.clone(); + let toast_autostart = toast_overlay.clone(); + let record_id_as = record.id; + autostart_row.connect_active_notify(move |row| { + if row.is_active() { + match integrator::enable_autostart(&db_autostart, &record_autostart) { + Ok(path) => { + log::info!("Autostart enabled: {}", path.display()); + toast_autostart.add_toast(adw::Toast::new("Will start at login")); + } + Err(e) => { + log::error!("Failed to enable autostart: {}", e); + toast_autostart.add_toast(adw::Toast::new("Failed to enable autostart")); + } + } + } else { + integrator::disable_autostart(&db_autostart, record_id_as).ok(); + toast_autostart.add_toast(adw::Toast::new("Autostart disabled")); + } + }); + integration_group.add(&autostart_row); + inner.append(&integration_group); // Version Rollback group