use std::fs; use std::io::{Read, Write}; use std::time::Duration; const TARGET: &str = "gpu-screen-recorder"; const POLL_INTERVAL: Duration = Duration::from_secs(2); /// Scans /proc for a process whose argv[0] basename matches the target. /// Reads cmdline (not the truncated `comm`) to avoid false positives /// from the 15-char comm truncation matching an unrelated binary. fn is_recording() -> bool { let entries = match fs::read_dir("/proc") { Ok(d) => d, Err(_) => return false, }; for entry in entries.flatten() { let name = entry.file_name(); let Some(pid_str) = name.to_str() else { continue; }; if !pid_str.bytes().all(|b| b.is_ascii_digit()) { continue; } let mut buf = Vec::with_capacity(64); if fs::File::open(format!("/proc/{pid_str}/cmdline")) .and_then(|mut f| f.read_to_end(&mut buf)) .is_err() { continue; } // cmdline is NUL-separated; argv[0] is everything before the first NUL let argv0 = buf.split(|&b| b == 0).next().unwrap_or(&[]); let argv0 = String::from_utf8_lossy(argv0); let basename = argv0.rsplit('/').next().unwrap_or(&argv0); if basename == TARGET { return true; } } false } fn emit(recording: bool) { let json = if recording { r#"{"text":"󰑊 Recording","class":"recording","alt":"recording","tooltip":"gpu-screen-recorder is running"}"# } else { r#"{"text":"","class":"idle","alt":"idle","tooltip":""}"# }; println!("{json}"); let _ = std::io::stdout().flush(); } fn main() { let mut last: Option = None; loop { let now = is_recording(); if last != Some(now) { emit(now); last = Some(now); } std::thread::sleep(POLL_INTERVAL); } }