Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 91 additions & 39 deletions crates/desktop/src/launcher.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use async_compat::Compat;
use std::{path::Path, time::Duration};

use directories::ProjectDirs;
use slint::PlatformError;
use tracing::info;
Expand All @@ -17,33 +18,7 @@ pub fn setup() -> Result<MainWindow, PlatformError> {
};
let lock_file = proj_dirs.data_local_dir().join(".rx.is.alive");

if lock_file.exists() {
eprintln!("Another instance of the application is already running.");
let api_port = std::fs::read_to_string(&lock_file).unwrap_or_else(|_| {
eprintln!("Failed to read lock file");
std::fs::remove_file(&lock_file).unwrap_or_else(|_| {
eprintln!("Failed to remove lock file");
});
std::process::exit(1);
});
eprintln!("Notify the other instance to raise...");
slint::spawn_local(Compat::new(async move {})).expect("Failed to spawn thread");
let client = reqwest::blocking::Client::new();
match client
.post(format!("http://127.0.0.1:{api_port}/popup"))
.header("User-Agent", format!("wsrx/{}", env!("CARGO_PKG_VERSION")))
.send()
{
Ok(_) => {
eprintln!("Notification sent.");
}
Err(e) => {
eprintln!("Failed to send notification: {e}, removing lock file.");
std::fs::remove_file(&lock_file).unwrap_or_else(|_| {
eprintln!("Failed to remove lock file");
});
}
}
if lock_file.exists() && try_focus_existing_instance(&lock_file) {
std::process::exit(0);
}

Expand All @@ -64,10 +39,82 @@ pub fn setup() -> Result<MainWindow, PlatformError> {
Ok(ui)
}

pub fn shutdown(ui: &slint::Weak<MainWindow>) {
let window = ui.upgrade().unwrap();
bridges::settings::save_config(&window);
daemon::save_scopes(ui);
fn try_focus_existing_instance(lock_file: &Path) -> bool {
eprintln!("Detected existing instance lock file. Trying to notify running app...");

let Some(api_port) = read_lock_file_port(lock_file) else {
return false;
};

match notify_existing_instance(api_port) {
Ok(()) => {
eprintln!("Notification sent.");
true
}
Err(err) => {
eprintln!("Failed to notify existing app: {err}. Removing stale lock file.");
remove_lock_file(lock_file);
false
}
}
}

fn remove_lock_file(lock_file: &Path) {
std::fs::remove_file(lock_file).unwrap_or_else(|err| {
if err.kind() != std::io::ErrorKind::NotFound {
eprintln!("Failed to remove lock file: {err}");
}
});
}

fn read_lock_file_port(lock_file: &Path) -> Option<u16> {
match std::fs::read_to_string(lock_file) {
Ok(port) => match port.trim().parse::<u16>() {
Ok(port) => Some(port),
Err(err) => {
eprintln!("Invalid lock file content: {err}. Removing stale lock file.");
remove_lock_file(lock_file);
None
}
},
Err(err) => {
eprintln!("Failed to read lock file: {err}. Removing stale lock file.");
remove_lock_file(lock_file);
None
}
}
}

fn notify_existing_instance(api_port: u16) -> Result<(), String> {
let client = reqwest::blocking::Client::builder()
.no_proxy()
.timeout(Duration::from_secs(2))
.build()
.unwrap_or_else(|err| {
eprintln!(
"Failed to create loopback HTTP client: {err}. Falling back to default client."
);
reqwest::blocking::Client::new()
});
Comment thread
Reverier-Xu marked this conversation as resolved.

let response = client
.post(format!("http://127.0.0.1:{api_port}/popup"))
.header("User-Agent", format!("wsrx/{}", env!("CARGO_PKG_VERSION")))
.send()
.map_err(|err| err.to_string())?;

if response.status().is_success() {
Ok(())
} else {
Err(format!("unexpected response status {}", response.status()))
}
}

pub fn cleanup_runtime_state(ui: &slint::Weak<MainWindow>) {
if let Some(window) = ui.upgrade() {
bridges::settings::save_config(&window);
daemon::save_scopes(ui);
}

let proj_dirs = match ProjectDirs::from("org", "xdsec", "wsrx") {
Some(dirs) => dirs,
Expand All @@ -77,15 +124,20 @@ pub fn shutdown(ui: &slint::Weak<MainWindow>) {
}
};

let log_dir = proj_dirs.data_local_dir().join("logs");
cleanup_runtime_files(proj_dirs.data_local_dir());
}

pub fn shutdown(ui: &slint::Weak<MainWindow>) {
cleanup_runtime_state(ui);
std::process::exit(0);
}

fn cleanup_runtime_files(data_local_dir: &Path) {
let log_dir = data_local_dir.join("logs");
std::fs::remove_dir_all(log_dir).unwrap_or_else(|_| {
eprintln!("Failed to remove log directory");
});

let lock_file = proj_dirs.data_local_dir().join(".rx.is.alive");
std::fs::remove_file(lock_file).unwrap_or_else(|_| {
eprintln!("Failed to remove lock file");
});

std::process::exit(0);
let lock_file = data_local_dir.join(".rx.is.alive");
remove_lock_file(&lock_file);
}
4 changes: 3 additions & 1 deletion crates/desktop/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ fn main() -> Result<(), Box<dyn Error>> {

// Create the main window.
let ui = launcher::setup()?;
let ui_weak = ui.as_weak();
ui.run().ok();
drop(console_guard);
launcher::cleanup_runtime_state(&ui_weak);
drop(file_guard);
drop(console_guard);
drop(ui);

Ok(())
Expand Down
Loading