From 71957c9d5d6275a327066f16ff5a61cebf31dc8f Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Thu, 21 Dec 2023 13:50:42 +0100 Subject: [PATCH] Finish single-instance support. Add tray-menu, on close confirmation dialog and a "running in background" notification --- tauri-app/src-tauri/Cargo.lock | 346 ++++++++++++++++++++++------ tauri-app/src-tauri/Cargo.toml | 5 +- tauri-app/src-tauri/src/main.rs | 302 ++++++++++++++++++------ tauri-app/src-tauri/tauri.conf.json | 25 +- tauri-app/src-tauri/web/index.html | 2 + 5 files changed, 525 insertions(+), 155 deletions(-) diff --git a/tauri-app/src-tauri/Cargo.lock b/tauri-app/src-tauri/Cargo.lock index 13321657..9b84c525 100644 --- a/tauri-app/src-tauri/Cargo.lock +++ b/tauri-app/src-tauri/Cargo.lock @@ -83,6 +83,54 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.75" @@ -103,13 +151,16 @@ dependencies = [ "glib-sys 0.18.1", "gtk-sys", "lazy_static", + "notify-rust", "rust-ini", "serde", "serde_json", "tauri", "tauri-build", + "tauri-plugin-cli", "tauri-plugin-clipboard-manager", "tauri-plugin-dialog", + "tauri-plugin-notification", "tauri-plugin-os", "tauri-plugin-shell", "tauri-plugin-single-instance", @@ -607,6 +658,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.31" @@ -635,6 +692,33 @@ dependencies = [ "vec_map", ] +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.10.0", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + [[package]] name = "clipboard-win" version = "4.5.0" @@ -646,22 +730,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics 0.22.3", - "foreign-types 0.3.2", - "libc", - "objc", -] - [[package]] name = "cocoa" version = "0.25.0" @@ -698,6 +766,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "combine" version = "4.6.6" @@ -970,7 +1044,7 @@ checksum = "17a1f14ed857323d318ca723a05a456196347efbe855f712f68cf6b8a14f8f15" dependencies = [ "atty", "base64 0.13.1", - "clap", + "clap 2.34.0", "encoding_rs", "percent-encoding", "url", @@ -1493,6 +1567,20 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib 0.18.4", + "libc", + "x11", +] + [[package]] name = "gdkx11-sys" version = "0.18.0" @@ -1941,7 +2029,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.51.1", ] [[package]] @@ -2293,6 +2381,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mac-notification-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fca4d74ff9dbaac16a01b924bc3693fa2bba0862c2c633abc73f9a8ea21f64" +dependencies = [ + "cc", + "dirs-next", + "objc-foundation", + "objc_id", + "time", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -2388,7 +2489,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b564d551449738387fb4541aef5fbfceaa81b2b732f2534c1c7c89dc7d673eaa" dependencies = [ - "cocoa 0.25.0", + "cocoa", "crossbeam-channel", "gtk", "keyboard-types", @@ -2453,6 +2554,19 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "notify-rust" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "827c5edfa80235ded4ab3fe8e9dc619b4f866ef16fe9b1c6b8a7f8692c0f2226" +dependencies = [ + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2874,7 +2988,7 @@ dependencies = [ "base64 0.21.5", "indexmap 2.1.0", "line-wrap", - "quick-xml", + "quick-xml 0.31.0", "serde", "time", ] @@ -2999,6 +3113,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.31.0" @@ -3764,26 +3887,19 @@ dependencies = [ [[package]] name = "tao" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f130523fee9820ad78141d443e6cef75043acade79107bc483872bc183928c0f" +checksum = "3c0dff18fed076d29cb5779e918ef4b8a5dbb756204e4a027794f0bce233d949" dependencies = [ "bitflags 1.3.2", - "cairo-rs", "cc", - "cocoa 0.24.1", + "cocoa", "core-foundation", - "core-graphics 0.22.3", + "core-graphics 0.23.1", "crossbeam-channel", "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib 0.18.4", - "glib-sys 0.18.1", "gtk", "image", "instant", @@ -3800,13 +3916,12 @@ dependencies = [ "png", "raw-window-handle", "scopeguard", - "serde", "tao-macros", "unicode-segmentation", "url", - "uuid", - "windows 0.51.1", + "windows 0.52.0", "windows-implement", + "windows-version", "x11-dl", "zbus", ] @@ -3830,13 +3945,13 @@ checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "tauri" -version = "2.0.0-alpha.18" +version = "2.0.0-alpha.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dc6ec8c246fa16092a3e650de2f10f3af3362915b7caab1aeed364a60829fcc" +checksum = "05fb63873c39d3fd5ddad995d395e7b7394ece0b69aeacb31e91d24af48f3de1" dependencies = [ "anyhow", "bytes", - "cocoa 0.25.0", + "cocoa", "dirs-next", "embed_plist", "futures-util", @@ -3845,14 +3960,16 @@ dependencies = [ "gtk", "heck", "http", + "ico", + "infer", "jni", "libc", "log", "mime", "muda", "objc", - "once_cell", "percent-encoding", + "png", "raw-window-handle", "reqwest", "serde", @@ -3873,14 +3990,14 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.51.1", + "windows 0.52.0", ] [[package]] name = "tauri-build" -version = "2.0.0-alpha.12" +version = "2.0.0-alpha.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0997a36aa2a1431500ef6ef92e7076521ae3258a8f73914b49ba876361ba2fe" +checksum = "a7a2582ffb43e5c28932c43ffc40c295a9196a9a33ffb1163269c6baed84834a" dependencies = [ "anyhow", "cargo_toml", @@ -3899,9 +4016,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.0.0-alpha.11" +version = "2.0.0-alpha.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c36db748f557c1f89f075e37ab22ad77e8798fb9432367cf4013ea364811de" +checksum = "b06976ec7b704d6b842169ffd4ce596e9ce45917a0ab462cb96a119fa2829be9" dependencies = [ "base64 0.21.5", "brotli", @@ -3925,9 +4042,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.0-alpha.11" +version = "2.0.0-alpha.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c895e684477cfb07aeeb0fdb11076bee98219806b68d1f3ddf99d893038a93" +checksum = "ff509be5a5ac34ec2e60d9029af1032c0a33e421f3e823bc92695192e2871c17" dependencies = [ "heck", "proc-macro2", @@ -3937,6 +4054,20 @@ dependencies = [ "tauri-utils", ] +[[package]] +name = "tauri-plugin-cli" +version = "2.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3ce9173dd3ae43c5c7529cce495e89cc9d8773adcd2a3e0efb5123aa052c64" +dependencies = [ + "clap 4.4.11", + "log", + "serde", + "serde_json", + "tauri", + "thiserror", +] + [[package]] name = "tauri-plugin-clipboard-manager" version = "2.0.0-alpha.5" @@ -3984,6 +4115,25 @@ dependencies = [ "uuid", ] +[[package]] +name = "tauri-plugin-notification" +version = "2.0.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e1cfe331495d0e72b9d48191eec98a54f9e189571b8ec6affb39b90b3df3bc" +dependencies = [ + "log", + "notify-rust", + "rand 0.8.5", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-build", + "thiserror", + "time", + "url", +] + [[package]] name = "tauri-plugin-os" version = "2.0.0-alpha.5" @@ -4036,9 +4186,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "1.0.0-alpha.5" +version = "1.0.0-alpha.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16cc441e5bcb3332a0af069b7580083104aacf09b66e27938b47517790d7b384" +checksum = "64a989e58af6e554dbac798a0a8d112faafc1509bcfab626466181e0724f09c5" dependencies = [ "gtk", "http", @@ -4049,34 +4199,35 @@ dependencies = [ "tauri-utils", "thiserror", "url", - "windows 0.51.1", + "windows 0.52.0", ] [[package]] name = "tauri-runtime-wry" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9229a7caf9c63eeaf4389789e1c983757135f4ece3355d0ae647c492682f61" +checksum = "5a9f181a6f5f982204ae293c19f37ba90116b8ec0bfd0a08c7a7ba67200cd9e3" dependencies = [ - "cocoa 0.24.1", + "cocoa", "gtk", "http", "jni", "percent-encoding", "raw-window-handle", + "tao", "tauri-runtime", "tauri-utils", "webkit2gtk", "webview2-com", - "windows 0.51.1", + "windows 0.52.0", "wry", ] [[package]] name = "tauri-utils" -version = "2.0.0-alpha.11" +version = "2.0.0-alpha.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce0dbf67341adad8d48255d605b45b25bf1c7445116355e61ed6219d204e94e0" +checksum = "f4858f99fc9f28b72008ef51d04d18b7e3646845c2bc18ee340045fed6ed5095" dependencies = [ "brotli", "ctor", @@ -4099,7 +4250,6 @@ dependencies = [ "thiserror", "url", "walkdir", - "windows-version", ] [[package]] @@ -4112,6 +4262,16 @@ dependencies = [ "toml 0.7.8", ] +[[package]] +name = "tauri-winrt-notification" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006851c9ccefa3c38a7646b8cec804bb429def3da10497bfa977179869c3e8e2" +dependencies = [ + "quick-xml 0.30.0", + "windows 0.51.1", +] + [[package]] name = "tempfile" version = "3.8.1" @@ -4407,7 +4567,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad962d06d2bfd9b2ab4f665fc73b175523b834b1466a294520201c5845145f8" dependencies = [ - "cocoa 0.25.0", + "cocoa", "core-graphics 0.23.1", "crossbeam-channel", "dirs-next", @@ -4504,6 +4664,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.6.1" @@ -4729,14 +4895,14 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15556ff1d1d6bc850dbb362762bae86069773dd30177c90d3bfa917080dc73" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.51.1", - "windows-core", + "windows 0.52.0", + "windows-core 0.52.0", "windows-implement", "windows-interface", ] @@ -4754,13 +4920,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3775bb005c3170497ec411b36005708b57ad486bfa3d23864c92f5973858ce8d" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" dependencies = [ "thiserror", - "windows 0.51.1", - "windows-core", + "windows 0.52.0", + "windows-core 0.52.0", ] [[package]] @@ -4815,7 +4981,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af6abc2b9c56bd95887825a1ce56cde49a2a97c07e28db465d541f5098a2656c" dependencies = [ - "cocoa 0.25.0", + "cocoa", "objc", "raw-window-handle", "windows-sys 0.52.0", @@ -4837,10 +5003,20 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows-core", + "windows-core 0.51.1", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", "windows-implement", "windows-interface", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -4852,11 +5028,20 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-implement" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2b158efec5af20d8846836622f50a87e6556b9153a42772fa047f773c0e555" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" dependencies = [ "proc-macro2", "quote", @@ -4865,9 +5050,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0546e63e1ce64c04403d2311fa0e3ab5ae3a367bd524b4a38d8d8d18c70cfa76" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" dependencies = [ "proc-macro2", "quote", @@ -5112,38 +5297,47 @@ dependencies = [ [[package]] name = "wry" -version = "0.34.2" +version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1e29660f22d8eec141f41f59b7fef231e4113c370c89b90ae3a0db8dec1927" +checksum = "a2ad1bc1d6925e0cde1bd01830b0073cd0448e21357e843b9ede33b6d81c7423" dependencies = [ "base64 0.21.5", "block", - "cocoa 0.25.0", + "cfg_aliases", + "cocoa", "core-graphics 0.23.1", "crossbeam-channel", "dunce", + "gdkx11", "gtk", "html5ever", "http", "javascriptcore-rs", + "jni", "kuchikiki", "libc", "log", + "ndk", + "ndk-context", + "ndk-sys", "objc", "objc_id", "once_cell", + "raw-window-handle", "serde", "serde_json", "sha2", "soup3", - "tao", + "tao-macros", "thiserror", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.51.1", + "windows 0.52.0", "windows-implement", + "windows-version", + "x11-dl", ] [[package]] diff --git a/tauri-app/src-tauri/Cargo.toml b/tauri-app/src-tauri/Cargo.toml index 41fb36fb..be0f53c9 100644 --- a/tauri-app/src-tauri/Cargo.toml +++ b/tauri-app/src-tauri/Cargo.toml @@ -17,12 +17,14 @@ tauri-build = { version = "2.0.0-alpha", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "2.0.0-alpha", features = [] } +tauri = { version = "2.0.0-alpha", features = ["tray-icon", "icon-ico", "icon-png"] } tauri-plugin-shell = "2.0.0-alpha" tauri-plugin-dialog = "2.0.0-alpha" tauri-plugin-clipboard-manager = "2.0.0-alpha" tauri-plugin-os = "2.0.0-alpha" tauri-plugin-single-instance = "2.0.0-alpha" +tauri-plugin-cli = "2.0.0-alpha" +tauri-plugin-notification = "2.0.0-alpha" dirs = "1.0" rust-ini = "0.20.0" @@ -37,6 +39,7 @@ uuid = "1.6.1" lazy_static = "1.4.0" tokio = "1.35.0" cached = "0.46.1" +notify-rust = "4.10.0" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/tauri-app/src-tauri/src/main.rs b/tauri-app/src-tauri/src/main.rs index de1bb2c6..0730e32f 100644 --- a/tauri-app/src-tauri/src/main.rs +++ b/tauri-app/src-tauri/src/main.rs @@ -2,106 +2,266 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod common; - use common::service_manager::*; -use tauri::http::response; -use tauri::{Manager, Window}; -use tauri::async_runtime::{spawn}; - +use tauri::{ + menu::{MenuBuilder, MenuItemBuilder, MenuItemKind, CheckMenuItem, CheckMenuItemBuilder, PredefinedMenuItem}, + tray::{ClickType, TrayIconBuilder}, + AppHandle, Manager, RunEvent, Window, WindowEvent, Icon, +}; +use tauri_plugin_cli::CliExt; +use tauri_plugin_dialog::DialogExt; +use tauri_plugin_notification::{NotificationExt, ActionType}; #[macro_use] extern crate lazy_static; #[derive(Clone, serde::Serialize)] struct Payload { - args: Vec, - cwd: String, + args: Vec, + cwd: String, } #[derive(Clone, serde::Serialize, serde::Deserialize)] struct Error { - error: String, + error: String, } #[tauri::command] -fn get_app_info(window: Window, response_id: String, matching_path: String, exec_path: String, pid: i64, cmdline: String) -> std::result::Result { - let mut id = response_id; - - let info = common::xdg_desktop::ProcessInfo{ - cmdline, - exec_path, - pid, - matching_path - }; - - if id == "" { - id = uuid::Uuid::new_v4().to_string().to_string(); - } - let cloned = id.clone(); - - std::thread::spawn(move || { - match common::xdg_desktop::get_app_info(info) { - Ok(info) => { - window.emit(&id, info) - }, - Err(err) => { - window.emit(&id, Error{ - error: err.to_string() - }) - } +fn get_app_info( + window: Window, + response_id: String, + matching_path: String, + exec_path: String, + pid: i64, + cmdline: String, +) -> std::result::Result { + let mut id = response_id; + + let info = common::xdg_desktop::ProcessInfo { + cmdline, + exec_path, + pid, + matching_path, + }; + + if id == "" { + id = uuid::Uuid::new_v4().to_string().to_string(); } - }); + let cloned = id.clone(); + + std::thread::spawn(move || match common::xdg_desktop::get_app_info(info) { + Ok(info) => window.emit(&id, info), + Err(err) => window.emit( + &id, + Error { + error: err.to_string(), + }, + ), + }); - Ok(cloned) + Ok(cloned) } -fn main() { - let systemd = SystemdServiceManager{}; +fn open_or_create_window(app: &AppHandle) -> Result<()> { + if let Some(window) = app.get_window("main") { + let _ = window.show(); + let _ = window.unminimize(); + let _ = window.set_focus(); + } else { + let _ = tauri::WindowBuilder::new(app, "main", tauri::WindowUrl::App("index.html".into())) + .build()?; + } + + Ok(()) +} + +fn setup_tray_menu(app: &mut tauri::App) -> core::result::Result<(), Box> { + // Tray menu + let close_btn = MenuItemBuilder::with_id("close", "Exit") + .build(app); + let open_btn = MenuItemBuilder::with_id("open", "Open").build(app); + + let spn = CheckMenuItemBuilder::with_id("spn", "SPN").build(app); + + + let menu = MenuBuilder::new(app) + .items(&[&spn, + &PredefinedMenuItem::separator(app), + &open_btn, + &close_btn + ]) + .build()?; - let output = systemd.status("portmaster.service"); + TrayIconBuilder::new() + .icon(Icon::Raw(include_bytes!("../../../notifier/icons/icons/pm_light_512.ico").into())) + .menu(&menu) + .on_menu_event(move |app, event| { + match event.id().as_ref() { + "close" => { + println!("showing dialog"); - match output { - Err(e) => panic!("Failed to execute systemctl: {e}"), - Ok(StatusResult::NotFound) => {println!("Service not found"); }, - Ok(StatusResult::Running) => println!("Service running"), - Ok(StatusResult::Stopped) => println!("Service stopped"), - Ok(StatusResult::Unsupported) => println!("Unsupported system service manager") - } + let handle = app.clone(); + app.dialog() + .message("This does not stop the Portmaster system service") + .title("Do you really want to quit the user interface") + .ok_button_label("Yes, exit") + .cancel_button_label("No") + .show(move |answer| { + if answer { + let _ = handle.emit("exit-requested", ""); + handle.exit(0); + } + }); + } + "open" => { + match open_or_create_window(app) { + Ok(_) => {} + Err(err) => { + eprintln!("Failed to open or create window: {:?}", err); + } + } + } + other => { + eprintln!("unknown menu event id: {}", other); + } + } + }) + .on_tray_icon_event(|tray, event| { // not supported on linux + if event.click_type == ClickType::Left { + let _ = open_or_create_window(tray.app_handle()); + } + }) + .build(app)?; - tauri::Builder::default() - // Shell plugin for open_external support - .plugin(tauri_plugin_shell::init()) + Ok(()) +} - // Clipboard support - .plugin(tauri_plugin_clipboard_manager::init()) +fn main() { + let systemd = SystemdServiceManager {}; - // Dialog (Save/Open) support - .plugin(tauri_plugin_dialog::init()) + let output = systemd.status("portmaster.service"); - // OS Version and Architecture support - .plugin(tauri_plugin_os::init()) - - // Single instance guard - .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { + match output { + Err(e) => panic!("Failed to execute systemctl: {e}"), + Ok(StatusResult::NotFound) => { + println!("Service not found"); + } + Ok(StatusResult::Running) => println!("Service running"), + Ok(StatusResult::Stopped) => println!("Service stopped"), + Ok(StatusResult::Unsupported) => println!("Unsupported system service manager"), + } + + let app = tauri::Builder::default() + // Shell plugin for open_external support + .plugin(tauri_plugin_shell::init()) + // Clipboard support + .plugin(tauri_plugin_clipboard_manager::init()) + // Dialog (Save/Open) support + .plugin(tauri_plugin_dialog::init()) + // OS Version and Architecture support + .plugin(tauri_plugin_os::init()) + // Single instance guard + .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { println!("{}, {argv:?}, {cwd}", app.package_info().name); - app.emit("single-instance", Payload { args: argv, cwd }).unwrap(); - })) + app.emit("single-instance", Payload { args: argv, cwd }) + .unwrap(); + })) + // Custom CLI arguments + .plugin(tauri_plugin_cli::init()) + // Notification support + .plugin(tauri_plugin_notification::init()) + // Custom Rust handlers + .invoke_handler(tauri::generate_handler![get_app_info]) + // Setup the app an any listeners + .setup(|app| { + setup_tray_menu(app)?; + + // Setup the single-instance event listener that will create/focus the main window + // incase the tauri app would have been started again. + let handle = app.handle().clone(); + app.listen_global("single-instance", move |_event| { + match handle.get_window("main") { + Some(window) => { + let _ = window.unminimize(); + let _ = window.show(); + let _ = window.set_focus(); + } - // Custom Rust handlers - .invoke_handler(tauri::generate_handler![get_app_info]) + None => { + let _ = open_or_create_window(&handle); + } + } + }); + + let mut background = false; + + match app.cli().matches() { + Ok(matches) => { + println!("{:?}", matches); + + if let Some(bg_flag) = matches.args.get("background") { + match bg_flag.value.as_bool() { + Some(value) => { + background = value; + } + None => {} + } + } + } + Err(_) => {} + }; + + if !background { + open_or_create_window(&app.handle().clone())?; + + #[cfg(debug_assertions)] + app.get_window("main").unwrap().open_devtools(); + } else { + let _ = app.notification() + .builder() + .action_type_id("test") + .body("Portmaster User Interface is running in the background") + .icon("portmaster") + .show(); + } + + Ok(()) + }) + .any_thread() + .build(tauri::generate_context!()) + .expect("error while running tauri application"); - .setup(|app| { - #[cfg(debug_assertions)] - { - let window = app.get_window("main").unwrap(); - window.open_devtools(); - } + app.run(|handle, e| match e { + RunEvent::WindowEvent { label, event, .. } => { + if label != "main" { + // We only have one window at most so any other label is unexpected + return; + } - Ok(()) + // Do not let the user close the window, instead send an event to the main + // window so we can show the "will not stop portmaster" dialog and let the window + // close itself using + // + // window.__TAURI__.window.getCurrent().close() + // + // Note: the above javascript does NOT trigger the CloseRequested event so + // there's no need to handle that case here. + // + match event { + WindowEvent::CloseRequested { api, .. } => { + api.prevent_close(); + if let Some(window) = handle.get_window(label.as_str()) { + let _ = window.emit("exit-requested", ""); + } + } + _ => {} + } + } + RunEvent::ExitRequested { api, .. } => { + api.prevent_exit(); + } + _ => {} }) - .any_thread() - .run(tauri::generate_context!()) - .expect("error while running tauri application"); } diff --git a/tauri-app/src-tauri/tauri.conf.json b/tauri-app/src-tauri/tauri.conf.json index fcfd7a1b..b7abd6e9 100644 --- a/tauri-app/src-tauri/tauri.conf.json +++ b/tauri-app/src-tauri/tauri.conf.json @@ -13,7 +13,25 @@ "productName": "Portmaster", "version": "0.1.0" }, + "plugins": { + "cli": { + "args": [ + { + "short": "d", + "name": "data", + "description": "Path to the installation directory", + "takesValue": true + }, + { + "short": "b", + "name": "background", + "description": "Start in the background without opening a window" + } + ] + } + }, "tauri": { + "bundle": { "active": true, "category": "DeveloperTool", @@ -73,13 +91,6 @@ ] }, "windows": [ - { - "fullscreen": false, - "height": 600, - "resizable": true, - "title": "Portmaster", - "width": 800 - } ] } } diff --git a/tauri-app/src-tauri/web/index.html b/tauri-app/src-tauri/web/index.html index 52b076cb..54535482 100644 --- a/tauri-app/src-tauri/web/index.html +++ b/tauri-app/src-tauri/web/index.html @@ -25,6 +25,8 @@ .addEventListener("click", loadImage); console.log("ready") + + window.__TAURI__.event.listen("exit-requested", () => window.__TAURI__.window.getCurrent().close()) }