diff --git a/Cargo.lock b/Cargo.lock index 8caedc7..539a480 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2886,7 +2886,7 @@ dependencies = [ [[package]] name = "rig-hub" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" dependencies = [ "anyhow", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 5eabf49..089e621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rig-hub" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" edition = "2024" license = "GPL-3.0-or-later" description = "One app for your mouse, cooler LCD, headset and mic on Linux" diff --git a/plasmoid/contents/ui/main.qml b/plasmoid/contents/ui/main.qml index 1c8aa59..6f69593 100644 --- a/plasmoid/contents/ui/main.qml +++ b/plasmoid/contents/ui/main.qml @@ -10,7 +10,7 @@ PlasmoidItem { // { panel: "85%", rows: [[label, value], …] } written by Rig Hub; null while it isn't running property var status: null // pgrep first: the file outlives Rig Hub, and stale numbers would look live - readonly property string poll: `pgrep -x rig-hub >/dev/null && cat "\${XDG_RUNTIME_DIR:-\${XDG_CACHE_HOME:-$HOME/.cache}}/rig-hub-status.json"` + readonly property string poll: `pgrep -x rig-hub >/dev/null && cat "\${XDG_RUNTIME_DIR:-\${XDG_CACHE_HOME:-$HOME/.cache}/rig-hub}/rig-hub-status.json"` // a second launch just brings the running one to the front readonly property string open: `PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH" setsid -f rig-hub` diff --git a/src/main.rs b/src/main.rs index 22cb31c..e535a64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -232,15 +232,27 @@ fn status_rows(st: &State) -> Vec<(String, String)> { rows } -/// Somewhere only this user can write: never a shared /tmp, where another user could plant files. -fn private_dir() -> PathBuf { - dirs::runtime_dir().or_else(dirs::cache_dir).unwrap_or_else(std::env::temp_dir) +/// Somewhere only this user can write, never a shared /tmp where another user could plant files +/// or symlinks. None if there's no such place: the status file and window raising are skipped. +fn private_dir() -> Option { + if let Some(dir) = dirs::runtime_dir() { + return Some(dir); // the system makes it owner-only + } + // ~/.cache can be group-writable under a loose umask, so use our own owner-only folder in it + let dir = dirs::cache_dir()?.join("rig-hub"); + std::fs::create_dir_all(&dir).ok()?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o700)).ok()?; + } + Some(dir) } /// For the KDE widget (`plasmoid/`), which polls this file while Rig Hub runs. The widget looks in -/// the same place: `${XDG_RUNTIME_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}}`. -fn status_path() -> PathBuf { - private_dir().join("rig-hub-status.json") +/// the same place: `${XDG_RUNTIME_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/rig-hub}`. +fn status_path() -> Option { + Some(private_dir()?.join("rig-hub-status.json")) } fn write_status(st: &State, rows: &[(String, String)]) -> bool { @@ -248,7 +260,9 @@ fn write_status(st: &State, rows: &[(String, String)]) -> bool { Some(audio::Battery::Level(p) | audio::Battery::Charging(Some(p))) => format!("{p}%"), _ => String::new(), }; - write_atomic(&status_path(), &serde_json::json!({ "panel": panel, "rows": rows }).to_string()) + // nowhere to write yet: false, so the next refresh tries again + let Some(path) = status_path() else { return false }; + write_atomic(&path, &serde_json::json!({ "panel": panel, "rows": rows }).to_string()) } /// Write then rename, so readers (the widget, Plasma) never see half a file. @@ -518,7 +532,7 @@ fn kwin_raise() { fn raise() { use std::io::Write; use std::os::unix::fs::OpenOptionsExt; - let script = private_dir().join("rig-hub-raise.js"); + let Some(script) = private_dir().map(|d| d.join("rig-hub-raise.js")) else { return }; let js = r#"for (const w of workspace.windowList()) if (w.resourceClass === "rig-hub") { w.minimized = false; workspace.activeWindow = w; }"#; // KWin runs whatever is in this file, so nobody else may be able to write it let _ = std::fs::remove_file(&script); @@ -1353,8 +1367,8 @@ fn main() -> eframe::Result { }, }; // numbers from a previous run would look live until our first refresh - if screenshot.is_none() { - let _ = std::fs::remove_file(status_path()); + if let (None, Some(path)) = (&screenshot, status_path()) { + let _ = std::fs::remove_file(path); } #[cfg(target_os = "linux")] install_widget();