From fed928889b74550ec55dac5d57bf519bef412b20 Mon Sep 17 00:00:00 2001 From: tu6ge Date: Sat, 30 Nov 2024 15:29:00 +0800 Subject: [PATCH] feat: auto start with os start (#33) * feat: auto start with os start * feat: shortcut default value enable --- package.json | 2 + pnpm-lock.yaml | 20 +++ src-tauri/Cargo.lock | 99 ++++++++++++- src-tauri/Cargo.toml | 4 + src-tauri/capabilities/desktop.json | 14 ++ src-tauri/src/lib.rs | 207 +++++++++++++++++++++++++++- src-tauri/src/main.rs | 90 +----------- 7 files changed, 342 insertions(+), 94 deletions(-) create mode 100644 src-tauri/capabilities/desktop.json diff --git a/package.json b/package.json index 99b015f..6d7f182 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "dependencies": { "@headlessui/react": "^2.1.10", "@tauri-apps/api": ">=2.0.0", + "@tauri-apps/plugin-autostart": "~2", + "@tauri-apps/plugin-global-shortcut": "~2", "@tauri-apps/plugin-http": "~2.0.1", "@tauri-apps/plugin-shell": ">=2.0.0", "@tauri-apps/plugin-websocket": "~2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9e2d15..b306735 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,12 @@ importers: '@tauri-apps/api': specifier: '>=2.0.0' version: 2.0.2 + '@tauri-apps/plugin-autostart': + specifier: ~2 + version: 2.0.0 + '@tauri-apps/plugin-global-shortcut': + specifier: ~2 + version: 2.0.0 '@tauri-apps/plugin-http': specifier: ~2.0.1 version: 2.0.1 @@ -686,6 +692,12 @@ packages: engines: {node: '>= 10'} hasBin: true + '@tauri-apps/plugin-autostart@2.0.0': + resolution: {integrity: sha512-NEwOQWVasZ8RczXkMLNJokRDujneuMH/UFA5t84DLkbNZUmiD3G7HZWhgSd1YQ0BFU9h9w+h2B/py3y6bzWg4Q==} + + '@tauri-apps/plugin-global-shortcut@2.0.0': + resolution: {integrity: sha512-pnB4CUwFVjg4twtBSxoLJ4uLFTYxsvOdC1zIbG581pYzhYatOl6mjB+ijD5SSXgiS/jNoqMcfkOF9PWAisurew==} + '@tauri-apps/plugin-http@2.0.1': resolution: {integrity: sha512-j6IA3pVBybSCwPpsihpX4z8bs6PluuGtr06ahL/xy4D8HunNBTmRmadJrFOQi0gOAbaig4MkQ15nzNLBLy8R1A==} @@ -2724,6 +2736,14 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.0.3 '@tauri-apps/cli-win32-x64-msvc': 2.0.3 + '@tauri-apps/plugin-autostart@2.0.0': + dependencies: + '@tauri-apps/api': 2.0.2 + + '@tauri-apps/plugin-global-shortcut@2.0.0': + dependencies: + '@tauri-apps/api': 2.0.2 + '@tauri-apps/plugin-http@2.0.1': dependencies: '@tauri-apps/api': 2.0.2 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index e23b0fa..df0449c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -91,6 +91,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto-launch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" +dependencies = [ + "dirs 4.0.0", + "thiserror", + "winreg 0.10.1", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -348,6 +359,8 @@ dependencies = [ "tauri", "tauri-build", "tauri-nspanel", + "tauri-plugin-autostart", + "tauri-plugin-global-shortcut", "tauri-plugin-http", "tauri-plugin-shell", "tauri-plugin-websocket", @@ -637,13 +650,33 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + [[package]] name = "dirs" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", ] [[package]] @@ -734,7 +767,7 @@ dependencies = [ "rustc_version", "toml 0.8.2", "vswhom", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -1161,6 +1194,23 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "global-hotkey" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00d88f1be7bf4cd2e61623ce08e84be2dfa4eab458e5d632d3dab95f16c1f64" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "objc2", + "objc2-app-kit", + "once_cell", + "serde", + "thiserror", + "windows-sys 0.59.0", + "x11-dl", +] + [[package]] name = "gobject-sys" version = "0.18.0" @@ -3363,7 +3413,7 @@ checksum = "5ce2818e803ce3097987296623ed8c0d9f65ed93b4137ff9a83e168bdbf62932" dependencies = [ "anyhow", "bytes", - "dirs", + "dirs 5.0.1", "dunce", "embed_plist", "futures-util", @@ -3413,7 +3463,7 @@ checksum = "935f9b3c49b22b3e2e485a57f46d61cd1ae07b1cbb2ba87387a387caf2d8c4e7" dependencies = [ "anyhow", "cargo_toml", - "dirs", + "dirs 5.0.1", "glob", "heck 0.5.0", "json-patch", @@ -3501,6 +3551,21 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-autostart" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bba6bb936e0fd0a58ed958b49e2e423dd40949c9d9425cc991be996959e3838e" +dependencies = [ + "auto-launch", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", +] + [[package]] name = "tauri-plugin-fs" version = "2.0.3" @@ -3522,6 +3587,21 @@ dependencies = [ "uuid", ] +[[package]] +name = "tauri-plugin-global-shortcut" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c15fb7f5e4c80a73ce97217dcff27e423f496178cbcb87e13b4efe99eebb550" +dependencies = [ + "global-hotkey", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", +] + [[package]] name = "tauri-plugin-http" version = "2.0.3" @@ -3917,7 +3997,7 @@ checksum = "533fc2d4105e0e3d96ce1c71f2d308c9fbbe2ef9c587cab63dd627ab5bde218f" dependencies = [ "core-graphics", "crossbeam-channel", - "dirs", + "dirs 5.0.1", "libappindicator", "muda", "objc2", @@ -4685,6 +4765,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index bf265f7..f5c3966 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -36,3 +36,7 @@ lto = true # Enables link-time-optimizations. opt-level = "s" # Prioritizes small binary size. Use `3` if you prefer speed. panic = "abort" # Higher performance by disabling panic handlers. strip = true # Ensures debug symbols are removed. + +[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-autostart = "2" +tauri-plugin-global-shortcut = "2" diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json new file mode 100644 index 0000000..2dff60b --- /dev/null +++ b/src-tauri/capabilities/desktop.json @@ -0,0 +1,14 @@ +{ + "identifier": "desktop-capability", + "platforms": [ + "macOS", + "windows", + "linux" + ], + "permissions": [ + "autostart:allow-enable", + "autostart:allow-disable", + "autostart:allow-is-enabled", + "global-shortcut:default" + ] +} \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0285de7..c645136 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,15 +1,220 @@ +use std::{fs::create_dir, io::Read}; + +use tauri::{AppHandle, Manager, Runtime, WebviewWindow}; +use tauri_nspanel::{panel_delegate, ManagerExt, WebviewWindowExt}; +use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut}; + +const DEFAULT_SHORTCUT: &str = "command+shift+space"; + // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! You've been greeted from Rust!", name) } +#[tauri::command] +fn change_window_height(handle: AppHandle, height: u32) { + let window: WebviewWindow = handle.get_webview_window("main").unwrap(); + + let mut size = window.outer_size().unwrap(); + size.height = height; + window.set_size(size).unwrap(); +} + +#[tauri::command] +fn show_panel(handle: AppHandle) { + let panel = handle.get_webview_panel("main").unwrap(); + + panel.show(); +} + +#[tauri::command] +fn hide_panel(handle: AppHandle) { + let panel = handle.get_webview_panel("main").unwrap(); + + panel.order_out(None); +} + +#[tauri::command] +fn close_panel(handle: AppHandle) { + let panel = handle.get_webview_panel("main").unwrap(); + + panel.released_when_closed(true); + + panel.close(); +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_shell::init()) - .invoke_handler(tauri::generate_handler![greet]) + .plugin(tauri_nspanel::init()) + .invoke_handler(tauri::generate_handler![ + greet, + change_window_height, + change_shortcut, + show_panel, + hide_panel, + close_panel, + ]) + .setup(|app| { + init(app.app_handle()); + + enable_autostart(app); + enable_shortcut(app); + + Ok(()) + }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } + +fn init(app_handle: &AppHandle) { + let window: WebviewWindow = app_handle.get_webview_window("main").unwrap(); + + let panel = window.to_panel().unwrap(); + + let delegate = panel_delegate!(MyPanelDelegate { + window_did_become_key, + window_did_resign_key + }); + + let handle = app_handle.to_owned(); + + delegate.set_listener(Box::new(move |delegate_name: String| { + match delegate_name.as_str() { + "window_did_become_key" => { + let app_name = handle.package_info().name.to_owned(); + + println!("[info]: {:?} panel becomes key window!", app_name); + } + "window_did_resign_key" => { + println!("[info]: panel resigned from key window!"); + } + _ => (), + } + })); + + panel.set_delegate(delegate); +} + +fn enable_autostart(app: &mut tauri::App) { + use tauri_plugin_autostart::MacosLauncher; + use tauri_plugin_autostart::ManagerExt; + + app.handle() + .plugin(tauri_plugin_autostart::init( + MacosLauncher::LaunchAgent, + None, + )) + .unwrap(); + + // Get the autostart manager + let autostart_manager = app.autolaunch(); + // Enable autostart + let _ = autostart_manager.enable(); +} + +fn enable_shortcut(app: &mut tauri::App) { + use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState}; + + let window = app.get_webview_window("main").unwrap(); + + let command_shortcut: Shortcut = current_shortcut(app.app_handle()).unwrap(); + + app.handle() + .plugin( + tauri_plugin_global_shortcut::Builder::new() + .with_handler(move |_app, shortcut, event| { + //println!("{:?}", shortcut); + if shortcut == &command_shortcut { + if let ShortcutState::Pressed = event.state() { + println!("Command+B Pressed!"); + if window.is_focused().unwrap() { + window.hide().unwrap(); + } else { + window.show().unwrap(); + window.set_focus().unwrap(); + } + } + } + }) + .build(), + ) + .unwrap(); + + app.global_shortcut().register(command_shortcut).unwrap(); +} + +#[tauri::command] +fn change_shortcut( + app: tauri::AppHandle, + window: tauri::Window, + key: String, +) -> Result<(), String> { + use std::fs::File; + use std::io::Write; + use tauri_plugin_global_shortcut::ShortcutState; + + remove_shortcut(&app)?; + + let shortcut: Shortcut = key.parse().map_err(|_| "设置的快捷键格式错误".to_owned())?; + app.global_shortcut() + .on_shortcut(shortcut, move |_app, scut, event| { + if scut == &shortcut { + if let ShortcutState::Pressed = event.state() { + println!("Command+B Pressed!"); + if window.is_focused().unwrap() { + window.hide().unwrap(); + } else { + window.show().unwrap(); + window.set_focus().unwrap(); + } + } + } + }) + .map_err(|_| "注册新的快捷键失败".to_owned())?; + + let path = app.path().app_config_dir().unwrap(); + if path.exists() == false { + create_dir(&path).unwrap(); + } + + let file_path = path.join("shortcut.txt"); + let mut file = File::create(file_path).unwrap(); + file.write_all(key.as_bytes()).unwrap(); + + Ok(()) +} + +fn current_shortcut(app: &tauri::AppHandle) -> Result { + use std::fs::File; + + let path = app.path().app_config_dir().unwrap(); + let old_value = if path.exists() == false { + DEFAULT_SHORTCUT.to_owned() + } else { + let file_path = path.join("shortcut.txt"); + let mut file = File::open(file_path).unwrap(); + let mut data = String::new(); + file.read_to_string(&mut data).unwrap(); + + if data.is_empty() { + DEFAULT_SHORTCUT.to_owned() + } else { + data + } + }; + let short: Shortcut = old_value.parse().unwrap(); + + Ok(short) +} + +fn remove_shortcut(app: &tauri::AppHandle) -> Result<(), String> { + let short = current_shortcut(app)?; + + app.global_shortcut().unregister(short).unwrap(); + + Ok(()) +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 6bb7487..29fc301 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,92 +1,6 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! -#![cfg_attr( - not(debug_assertions), - windows_subsystem = "windows" -)] - -use tauri::{AppHandle, Manager, WebviewWindow}; -use tauri_nspanel::{panel_delegate, ManagerExt, WebviewWindowExt}; +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] fn main() { - coco_lib::run(); - - tauri::Builder::default() - .plugin(tauri_plugin_websocket::init()) - .plugin(tauri_plugin_http::init()) - .plugin(tauri_nspanel::init()) - .invoke_handler(tauri::generate_handler![ - show_panel, - hide_panel, - close_panel, - change_window_height - ]) - .setup(|app| { - init(app.app_handle()); - - Ok(()) - }) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} - -fn init(app_handle: &AppHandle) { - let window: WebviewWindow = app_handle.get_webview_window("main").unwrap(); - - let panel = window.to_panel().unwrap(); - - let delegate = panel_delegate!(MyPanelDelegate { - window_did_become_key, - window_did_resign_key - }); - - let handle = app_handle.to_owned(); - - delegate.set_listener(Box::new(move |delegate_name: String| { - match delegate_name.as_str() { - "window_did_become_key" => { - let app_name = handle.package_info().name.to_owned(); - - println!("[info]: {:?} panel becomes key window!", app_name); - } - "window_did_resign_key" => { - println!("[info]: panel resigned from key window!"); - } - _ => (), - } - })); - - panel.set_delegate(delegate); -} - - -#[tauri::command] -fn change_window_height(handle: AppHandle,height: u32) { - let window: WebviewWindow = handle.get_webview_window("main").unwrap(); - - let mut size = window.outer_size().unwrap(); - size.height = height; - window.set_size(size).unwrap(); -} - -#[tauri::command] -fn show_panel(handle: AppHandle) { - let panel = handle.get_webview_panel("main").unwrap(); - - panel.show(); -} - -#[tauri::command] -fn hide_panel(handle: AppHandle) { - let panel = handle.get_webview_panel("main").unwrap(); - - panel.order_out(None); -} - -#[tauri::command] -fn close_panel(handle: AppHandle) { - let panel = handle.get_webview_panel("main").unwrap(); - - panel.released_when_closed(true); - - panel.close(); + coco_lib::run(); }