From 19d1c1ef7a2e8712d36d40440fabda3c4ddb8bcd Mon Sep 17 00:00:00 2001 From: kwaroran Date: Wed, 9 Oct 2024 21:12:06 +0900 Subject: [PATCH] Add tauri_plugin_single_instance --- src-tauri/Cargo.lock | 46 +++++++++++++++++ src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 116 ++++++++++++++++++++++-------------------- 3 files changed, 109 insertions(+), 54 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c4ff6e9a..6e8ea0ec 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -127,6 +127,30 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + [[package]] name = "async-io" version = "2.3.4" @@ -3508,6 +3532,7 @@ dependencies = [ "tauri-plugin-os", "tauri-plugin-process", "tauri-plugin-shell", + "tauri-plugin-single-instance", "tauri-plugin-updater", "tiktoken-rs", "url", @@ -4519,6 +4544,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-single-instance" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25ac834491d089699a2bc9266a662faf373c9f779f05a2235bc6e4d9e61769a" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "thiserror", + "windows-sys 0.59.0", + "zbus", +] + [[package]] name = "tauri-plugin-updater" version = "2.0.2" @@ -5872,9 +5912,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" dependencies = [ "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", "async-process", "async-recursion", + "async-task", "async-trait", + "blocking", "derivative", "enumflags2", "event-listener", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b04fe1d6..6b462a53 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -42,4 +42,5 @@ custom-protocol = ["tauri/custom-protocol"] # crate-type = ["staticlib", "cdylib", "rlib", "lib"] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-single-instance = "2" tauri-plugin-updater = "2" diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 6a32ad96..4c620aff 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -10,10 +10,10 @@ use base64::{engine::general_purpose, Engine as _}; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use serde_json::json; use serde_json::Value; -use tauri::path::BaseDirectory; use std::collections::HashMap; use std::io::Write; use std::{path::Path, time::Duration}; +use tauri::path::BaseDirectory; use tauri::Manager; use tauri::{AppHandle, Emitter}; @@ -305,7 +305,7 @@ fn run_py_server(handle: tauri::AppHandle, py_path: String) { let py_exec_path = Path::new(&py_path).join("python").join("python.exe"); let server_path = handle .path() - .resolve("src-python/run.py", BaseDirectory::Resource) + .resolve("src-python/run.py", BaseDirectory::Resource) .expect("failed to resolve resource"); let mut py_server = Command::new(&py_exec_path); @@ -326,14 +326,13 @@ fn run_py_server(handle: tauri::AppHandle, py_path: String) { return; } - #[tauri::command] async fn streamed_fetch( id: String, url: String, headers: String, body: String, - app: AppHandle + app: AppHandle, ) -> String { //parse headers let headers_json: Value = match serde_json::from_str(&headers) { @@ -419,50 +418,60 @@ async fn streamed_fetch( } } - use std::path::PathBuf; // Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT fn handle_file_associations(app: AppHandle, files: Vec) { // -- Scope handling start -- - + // You can remove this block if you only want to know about the paths, but not actually "use" them in the frontend. - + // This requires the `fs` tauri plugin and is required to make the plugin's frontend work: // use tauri_plugin_fs::FsExt; // let fs_scope = app.fs_scope(); - + // This is for the `asset:` protocol to work: let asset_protocol_scope = app.asset_protocol_scope(); - + for file in &files { - // This requires the `fs` plugin: - // let _ = fs_scope.allow_file(file); - - // This is for the `asset:` protocol: - let _ = asset_protocol_scope.allow_file(file); + // This requires the `fs` plugin: + // let _ = fs_scope.allow_file(file); + + // This is for the `asset:` protocol: + let _ = asset_protocol_scope.allow_file(file); } - + // -- Scope handling end -- - + let files = files - .into_iter() - .map(|f| { - let file = f.to_string_lossy().replace("\\", "\\\\"); // escape backslash - format!("\"{file}\"",) // wrap in quotes for JS array - }) - .collect::>() - .join(","); - + .into_iter() + .map(|f| { + let file = f.to_string_lossy().replace("\\", "\\\\"); // escape backslash + format!("\"{file}\"",) // wrap in quotes for JS array + }) + .collect::>() + .join(","); + tauri::WebviewWindowBuilder::new(&app, "main", Default::default()) - .initialization_script(&format!("window.tauriOpenedFiles = [{files}]")) - .build() - .unwrap(); - } + .initialization_script(&format!("window.tauriOpenedFiles = [{files}]")) + .build() + .unwrap(); +} fn main() { - tauri::Builder::default() + let mut builder = tauri::Builder::default(); + + #[cfg(desktop)] + { + builder = builder.plugin(tauri_plugin_single_instance::init(|app, args, cwd| { + let _ = app.get_webview_window("main") + .expect("no main window") + .set_focus(); + })); + } + + builder .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_process::init()) @@ -474,29 +483,29 @@ fn main() { #[cfg(any(windows, target_os = "linux"))] { let mut files = Vec::new(); - + // NOTICE: `args` may include URL protocol (`your-app-protocol://`) // or arguments (`--`) if your app supports them. // files may aslo be passed as `file://path/to/file` for maybe_file in std::env::args().skip(1) { - // skip flags like -f or --flag - if maybe_file.starts_with("-") { - continue; - } - - // handle `file://` path urls and skip other urls - if let Ok(url) = url::Url::parse(&maybe_file) { - if let Ok(path) = url.to_file_path() { - files.push(path); + // skip flags like -f or --flag + if maybe_file.starts_with("-") { + continue; + } + + // handle `file://` path urls and skip other urls + if let Ok(url) = url::Url::parse(&maybe_file) { + if let Ok(path) = url.to_file_path() { + files.push(path); + } + } else { + files.push(PathBuf::from(maybe_file)) } - } else { - files.push(PathBuf::from(maybe_file)) } - } - + handle_file_associations(app.handle().clone(), files); } - + Ok(()) }) .invoke_handler(tauri::generate_handler![ @@ -519,18 +528,17 @@ fn main() { // SPDX-License-Identifier: MIT #[allow(unused_variables)] |app, event| { - #[cfg(any(target_os = "macos", target_os = "ios"))] - if let tauri::RunEvent::Opened { urls } = event { - let files = urls - .into_iter() - .filter_map(|url| url.to_file_path().ok()) - .collect::>(); - - handle_file_associations(app.clone(), files); - } + #[cfg(any(target_os = "macos", target_os = "ios"))] + if let tauri::RunEvent::Opened { urls } = event { + let files = urls + .into_iter() + .filter_map(|url| url.to_file_path().ok()) + .collect::>(); + + handle_file_associations(app.clone(), files); + } }, ); - } fn header_map_to_json(header_map: &HeaderMap) -> serde_json::Value {