// Copyright 2019-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT #[cfg(feature = "semver")] use crate::semver_compat::semver_compat_string; use crate::SingleInstanceCallback; use tauri::{ plugin::{self, TauriPlugin}, AppHandle, Config, Manager, RunEvent, Runtime, }; use zbus::{ blocking::{connection::Builder, Connection}, interface, }; struct ConnectionHandle(Connection); struct SingleInstanceDBus { callback: Box>, app_handle: AppHandle, } #[interface(name = "org.SingleInstance.DBus")] impl SingleInstanceDBus { fn execute_callback(&mut self, argv: Vec, cwd: String) { (self.callback)(&self.app_handle, argv, cwd); } } #[cfg(feature = "semver")] fn dbus_id(config: &Config, version: semver::Version) -> String { let mut id = config.identifier.replace(['.', '-'], "_"); id.push('_'); id.push_str(semver_compat_string(version).as_str()); id } #[cfg(not(feature = "semver"))] fn dbus_id(config: &Config) -> String { config.identifier.replace(['.', '-'], "_") } pub fn init(f: Box>) -> TauriPlugin { plugin::Builder::new("single-instance") .setup(|app, _api| { #[cfg(feature = "semver")] let id = dbus_id(app.config(), app.package_info().version.clone()); #[cfg(not(feature = "semver"))] let id = dbus_id(app.config()); let single_instance_dbus = SingleInstanceDBus { callback: f, app_handle: app.clone(), }; let dbus_name = format!("org.{id}.SingleInstance"); let dbus_path = format!("/org/{id}/SingleInstance"); match Builder::session() .unwrap() .name(dbus_name.as_str()) .unwrap() .serve_at(dbus_path.as_str(), single_instance_dbus) .unwrap() .build() { Ok(connection) => { app.manage(ConnectionHandle(connection)); } Err(zbus::Error::NameTaken) => { if let Ok(connection) = Connection::session() { let _ = connection.call_method( Some(dbus_name.as_str()), dbus_path.as_str(), Some("org.SingleInstance.DBus"), "ExecuteCallback", &( std::env::args().collect::>(), std::env::current_dir() .unwrap_or_default() .to_str() .unwrap_or_default(), ), ); } app.cleanup_before_exit(); std::process::exit(0); } _ => {} } Ok(()) }) .on_event(|app, event| { if let RunEvent::Exit = event { destroy(app); } }) .build() } pub fn destroy>(manager: &M) { if let Some(connection) = manager.try_state::() { #[cfg(feature = "semver")] let id = dbus_id( manager.config(), manager.app_handle().package_info().version.clone(), ); #[cfg(not(feature = "semver"))] let id = dbus_id(manager.config()); let dbus_name = format!("org.{id}.SingleInstance",); let _ = connection.0.release_name(dbus_name); } }