From 568594860815502286505cc5be1518fd1316c162 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Sat, 9 Sep 2023 07:29:57 -0700 Subject: [PATCH] add hotkeys to show window and launch terminal --- src-tauri/Cargo.toml | 2 +- src-tauri/src/app.rs | 2 ++ src-tauri/src/config.rs | 58 +++++++++++++++++++++++++++++++++++++++++ src-tauri/src/errors.rs | 2 ++ src-tauri/src/state.rs | 6 +++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 564e032..9c78901 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -25,7 +25,7 @@ tauri-build = { version = "1.0.4", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.2", features = ["dialog", "dialog-open", "os-all", "system-tray"] } +tauri = { version = "1.2", features = ["dialog", "dialog-open", "os-all", "system-tray", "global-shortcut"] } tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } sodiumoxide = "0.2.7" tokio = { version = ">=1.19", features = ["full"] } diff --git a/src-tauri/src/app.rs b/src-tauri/src/app.rs index 0d2fe0a..4702646 100644 --- a/src-tauri/src/app.rs +++ b/src-tauri/src/app.rs @@ -10,6 +10,7 @@ use tauri::{ App, AppHandle, Manager, + GlobalShortcutManager, async_runtime as rt, }; @@ -83,6 +84,7 @@ async fn setup(app: &mut App) -> Result<(), Box> { let srv = Server::new(conf.listen_addr, conf.listen_port, app.handle()).await?; config::set_auto_launch(conf.start_on_login)?; + config::register_hotkeys(&conf.hotkeys)?; // if session is empty, this is probably the first launch, so don't autohide if !conf.start_minimized || is_first_launch { app.get_window("main") diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index ffb6bc8..dc20103 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -5,6 +5,12 @@ use auto_launch::AutoLaunchBuilder; use is_terminal::IsTerminal; use serde::{Serialize, Deserialize}; use sqlx::SqlitePool; +use tauri::{ + AppHandle, + Manager, + GlobalShortcutManager, + async_runtime as rt, +}; use crate::errors::*; @@ -20,6 +26,14 @@ pub struct TermConfig { } +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct HotkeyConfig { + // tauri uses strings to represent keybinds, so we will as well + pub show_window: String, + pub launch_terminal: String, +} + + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct AppConfig { #[serde(default = "default_listen_addr")] @@ -34,6 +48,8 @@ pub struct AppConfig { pub start_on_login: bool, #[serde(default = "default_term_config")] pub terminal: TermConfig, + #[serde(default = "default_hotkey_config")] + pub hotkeys: HotkeyConfig, } @@ -46,6 +62,7 @@ impl Default for AppConfig { start_minimized: default_start_minimized(), start_on_login: default_start_on_login(), terminal: default_term_config(), + hotkeys: default_hotkey_config(), } } } @@ -170,6 +187,47 @@ fn default_term_config() -> TermConfig { } +fn default_hotkey_config() -> HotkeyConfig { + HotkeyConfig { + show_window: "alt+shift+C".into(), + launch_terminal: "alt+shift+T".into(), + } +} + +// note: will panic if called before APP is set +pub fn register_hotkeys(hotkeys: &HotkeyConfig) -> tauri::Result<()> { + let app = crate::app::APP.get().unwrap(); + + let mut manager = app.global_shortcut_manager(); + manager.unregister_all()?; + + let h = app.app_handle(); + manager.register( + &hotkeys.show_window, + move || { + h.get_window("main") + .map(|w| w.show().error_popup("Failed to show")) + .ok_or(HandlerError::NoMainWindow) + .error_popup("No main window"); + }, + )?; + + // register() doesn't take an async fn, so we have to use spawn + manager.register( + &hotkeys.launch_terminal, + || { + rt::spawn(async { + crate::terminal::launch(false) + .await + .error_popup("Failed to launch"); + }); + } + )?; + + Ok(()) +} + + fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST } fn default_rehide_ms() -> u64 { 1000 } // start minimized and on login only in production mode diff --git a/src-tauri/src/errors.rs b/src-tauri/src/errors.rs index 53f0c62..f564917 100644 --- a/src-tauri/src/errors.rs +++ b/src-tauri/src/errors.rs @@ -95,6 +95,8 @@ pub enum SetupError { ServerSetupError(#[from] std::io::Error), #[error("Failed to resolve data directory: {0}")] DataDir(#[from] DataDirError), + #[error("Failed to register hotkeys: {0}")] + RegisterHotkeys(#[from] tauri::Error), } diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 5d30d5d..ffb2c9f 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -59,15 +59,21 @@ impl AppState { pub async fn update_config(&self, new_config: AppConfig) -> Result<(), SetupError> { let mut live_config = self.config.write().await; + // update autostart if necessary if new_config.start_on_login != live_config.start_on_login { config::set_auto_launch(new_config.start_on_login)?; } + // rebind socket if necessary if new_config.listen_addr != live_config.listen_addr || new_config.listen_port != live_config.listen_port { let mut sv = self.server.write().await; sv.rebind(new_config.listen_addr, new_config.listen_port).await?; } + // re-register hotkeys if necessary + if new_config.hotkeys != live_config.hotkeys { + config::register_hotkeys(&new_config.hotkeys)?; + } new_config.save(&self.pool).await?; *live_config = new_config;