Skip to main content

tauri_plugin_single_instance/
lib.rs

1// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! Ensure a single instance of your tauri app is running.
6//!
7//! ## Cargo features
8//!
9//! - **semver**: Allows the app with SemVer incompatible versions to run alongside each other.
10//! - **deep-link**: Trigger [`tauri-plugin-deep-link`](https://crates.io/crates/tauri-plugin-deep-link) event before invoking the single-instance callback.
11
12#![doc(
13    html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
14    html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
15)]
16#![cfg(not(any(target_os = "android", target_os = "ios")))]
17
18use tauri::{plugin::TauriPlugin, AppHandle, Manager, Runtime};
19
20#[cfg(target_os = "windows")]
21#[path = "platform_impl/windows.rs"]
22mod platform_impl;
23#[cfg(target_os = "linux")]
24#[path = "platform_impl/linux.rs"]
25mod platform_impl;
26#[cfg(target_os = "macos")]
27#[path = "platform_impl/macos.rs"]
28mod platform_impl;
29
30#[cfg(feature = "semver")]
31mod semver_compat;
32
33pub(crate) type SingleInstanceCallback<R> =
34    dyn FnMut(&AppHandle<R>, Vec<String>, String) + Send + Sync + 'static;
35
36pub fn init<R: Runtime, F: FnMut(&AppHandle<R>, Vec<String>, String) + Send + Sync + 'static>(
37    f: F,
38) -> TauriPlugin<R> {
39    Builder::new().callback(f).build()
40}
41
42pub fn destroy<R: Runtime, M: Manager<R>>(manager: &M) {
43    platform_impl::destroy(manager)
44}
45
46pub struct Builder<R: Runtime> {
47    callback: Box<SingleInstanceCallback<R>>,
48    dbus_id: Option<String>,
49}
50
51impl<R: Runtime> Default for Builder<R> {
52    fn default() -> Self {
53        Self {
54            callback: Box::new(move |_app, _args, _| {
55                #[cfg(feature = "deep-link")]
56                if let Some(deep_link) = _app.try_state::<tauri_plugin_deep_link::DeepLink<R>>() {
57                    deep_link.handle_cli_arguments(_args.iter());
58                }
59            }),
60            dbus_id: None,
61        }
62    }
63}
64
65impl<R: Runtime> Builder<R> {
66    pub fn new() -> Self {
67        Default::default()
68    }
69
70    /// Function to call when a secondary instance was opened by the user and killed by the plugin.
71    /// If the `deep-link` feature is enabled, the plugin triggers the deep-link plugin before executing the callback.
72    pub fn callback<F: FnMut(&AppHandle<R>, Vec<String>, String) + Send + Sync + 'static>(
73        mut self,
74        mut f: F,
75    ) -> Self {
76        self.callback = Box::new(move |app, args, cwd| {
77            #[cfg(feature = "deep-link")]
78            if let Some(deep_link) = app.try_state::<tauri_plugin_deep_link::DeepLink<R>>() {
79                deep_link.handle_cli_arguments(args.iter());
80            }
81            f(app, args, cwd)
82        });
83        self
84    }
85
86    /// Set a custom D-Bus ID, used on Linux. The plugin will append a `.SingleInstance` subname.
87    /// For example `com.mycompany.myapp` will result in the plugin registering its D-Bus service on `com.mycompany.myapp.SingleInstance`.
88    /// Usually you want the same base ID across all components in your app.
89    ///
90    /// Defaults to the app's bundle identifier set in tauri.conf.json.
91    pub fn dbus_id(mut self, dbus_id: impl Into<String>) -> Self {
92        self.dbus_id = Some(dbus_id.into());
93        self
94    }
95
96    pub fn build(self) -> TauriPlugin<R> {
97        platform_impl::init(
98            self.callback,
99            #[cfg(target_os = "linux")]
100            self.dbus_id,
101        )
102    }
103}