feat(owlry): implement toggle behavior for repeated invocations
Use a flock-based lock file at $XDG_RUNTIME_DIR/owlry/owlry-ui.lock to detect when another owlry UI instance is already running. If the lock is held, send a Toggle IPC command to the daemon and exit immediately instead of opening a second window.
This commit is contained in:
@@ -10,10 +10,43 @@ mod ui;
|
||||
use app::OwlryApp;
|
||||
use cli::{CliArgs, Command};
|
||||
use log::{info, warn};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
#[cfg(feature = "dev-logging")]
|
||||
use log::debug;
|
||||
|
||||
/// Try to acquire an exclusive lock on the UI lock file.
|
||||
///
|
||||
/// Returns `Some(File)` if the lock was acquired (no other instance running),
|
||||
/// or `None` if another instance already holds the lock.
|
||||
/// The returned `File` must be kept alive for the duration of the process.
|
||||
fn try_acquire_lock() -> Option<std::fs::File> {
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
let lock_path = owlry_core::paths::socket_path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("owlry-ui.lock");
|
||||
|
||||
// Ensure the parent directory exists
|
||||
if let Some(parent) = lock_path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
|
||||
std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.mode(0o600)
|
||||
.open(&lock_path)
|
||||
.ok()
|
||||
.and_then(|f| {
|
||||
let fd = f.as_raw_fd();
|
||||
let ret = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
|
||||
if ret == 0 { Some(f) } else { None }
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = CliArgs::parse_args();
|
||||
|
||||
@@ -46,6 +79,27 @@ fn main() {
|
||||
debug!("CLI args: {:?}", args);
|
||||
}
|
||||
|
||||
// Toggle behavior: if another instance is already running, tell the daemon
|
||||
// to toggle visibility and exit immediately.
|
||||
let _lock_guard = match try_acquire_lock() {
|
||||
Some(file) => file,
|
||||
None => {
|
||||
// Another instance holds the lock — send toggle to daemon and exit
|
||||
info!("Another owlry instance detected, sending toggle");
|
||||
let socket_path = client::CoreClient::socket_path();
|
||||
if let Ok(mut client) = client::CoreClient::connect(&socket_path) {
|
||||
if let Err(e) = client.toggle() {
|
||||
eprintln!("Failed to toggle existing instance: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
eprintln!("Another instance is running but daemon is unreachable");
|
||||
std::process::exit(1);
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
info!("Starting Owlry launcher");
|
||||
|
||||
// Diagnostic: log critical environment variables
|
||||
|
||||
Reference in New Issue
Block a user