mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-27 11:56:05 +02:00
dabac0eedf
* feat(single-instance): add macos implementation * chore(single-instance): test MacOs by adding CLI to example * feat(single-instance): simplify macOS implementation * chore(single-instance): address remarks
123 lines
4.0 KiB
Rust
123 lines
4.0 KiB
Rust
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#![cfg(target_os = "macos")]
|
|
|
|
use std::{
|
|
io::{BufWriter, Error, ErrorKind, Read, Write},
|
|
os::unix::net::{UnixListener, UnixStream},
|
|
path::PathBuf,
|
|
};
|
|
|
|
use crate::SingleInstanceCallback;
|
|
use tauri::{
|
|
plugin::{self, TauriPlugin},
|
|
AppHandle, Config, Manager, RunEvent, Runtime,
|
|
};
|
|
|
|
pub fn init<R: Runtime>(cb: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
|
|
plugin::Builder::new("single-instance")
|
|
.setup(|app, _api| {
|
|
let socket = socket_path(app.config());
|
|
|
|
// Notify the singleton which may or may not exist.
|
|
match notify_singleton(&socket) {
|
|
Ok(_) => {
|
|
std::process::exit(0);
|
|
}
|
|
Err(e) => {
|
|
match e.kind() {
|
|
ErrorKind::NotFound | ErrorKind::ConnectionRefused => {
|
|
// This process claims itself as singleton as likely none exists
|
|
socket_cleanup(&socket);
|
|
listen_for_other_instances(&socket, app.clone(), cb);
|
|
}
|
|
_ => {
|
|
log::debug!(
|
|
"single_instance failed to notify - launching normally: {}",
|
|
e
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
})
|
|
.on_event(|app, event| {
|
|
if let RunEvent::Exit = event {
|
|
destroy(app);
|
|
}
|
|
})
|
|
.build()
|
|
}
|
|
|
|
pub fn destroy<R: Runtime, M: Manager<R>>(manager: &M) {
|
|
let socket = socket_path(manager.config());
|
|
socket_cleanup(&socket);
|
|
}
|
|
|
|
fn socket_path(config: &Config) -> PathBuf {
|
|
let identifier = config.identifier.replace(['.', '-'].as_ref(), "_");
|
|
// Use /tmp as socket path must be shorter than 100 chars.
|
|
PathBuf::from(format!("/tmp/{}_si.sock", identifier))
|
|
}
|
|
|
|
fn socket_cleanup(socket: &PathBuf) {
|
|
let _ = std::fs::remove_file(socket);
|
|
}
|
|
|
|
fn notify_singleton(socket: &PathBuf) -> Result<(), Error> {
|
|
let stream = UnixStream::connect(&socket)?;
|
|
let mut bf = BufWriter::new(&stream);
|
|
let args_joined = std::env::args().collect::<Vec<String>>().join("\0");
|
|
bf.write_all(args_joined.as_bytes())?;
|
|
bf.flush()?;
|
|
drop(bf);
|
|
Ok(())
|
|
}
|
|
|
|
fn listen_for_other_instances<A: Runtime>(
|
|
socket: &PathBuf,
|
|
app: AppHandle<A>,
|
|
mut cb: Box<SingleInstanceCallback<A>>,
|
|
) {
|
|
match UnixListener::bind(&socket) {
|
|
Ok(listener) => {
|
|
let cwd = std::env::current_dir()
|
|
.unwrap_or_default()
|
|
.to_str()
|
|
.unwrap_or_default()
|
|
.to_string();
|
|
|
|
tauri::async_runtime::spawn(async move {
|
|
for stream in listener.incoming() {
|
|
match stream {
|
|
Ok(mut stream) => {
|
|
let mut s = String::new();
|
|
match stream.read_to_string(&mut s) {
|
|
Ok(_) => {
|
|
let args: Vec<String> =
|
|
s.split('\0').map(String::from).collect();
|
|
cb(&app.clone().app_handle(), args, cwd.clone());
|
|
}
|
|
Err(e) => log::debug!("single_instance failed to be notified: {e}"),
|
|
}
|
|
}
|
|
Err(err) => {
|
|
log::debug!("single_instance failed to be notified: {}", err);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
Err(err) => {
|
|
log::error!(
|
|
"single_instance failed to listen to other processes - launching normally: {}",
|
|
err
|
|
);
|
|
}
|
|
}
|
|
}
|