diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c7fec91..2c23028 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2349,6 +2349,7 @@ dependencies = [ "rustfft", "serde", "serde_json", + "signal-hook", "strsim", "tar", "tauri", @@ -5560,6 +5561,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.6" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ec8443a..ba237a6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -29,10 +29,10 @@ tauri-build = { version = "2", features = [] } [dependencies] once_cell = "1" tauri = { version = "2.9.1", features = [ - "protocol-asset", - "macos-private-api", - "tray-icon", - 'image-png', + "protocol-asset", + "macos-private-api", + "tray-icon", + 'image-png', ] } tauri-plugin-log = "2.7.1" tauri-plugin-opener = "2.5.2" @@ -68,6 +68,9 @@ tar = "0.4.44" flate2 = "1.0" transcribe-rs = "0.1.4" +[target.'cfg(unix)'.dependencies] +signal-hook = "0.3" + [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-autostart = "2.5.1" tauri-plugin-global-shortcut = "2.3.1" @@ -76,9 +79,9 @@ tauri-plugin-updater = "2.9.0" [target.'cfg(windows)'.dependencies] windows = { version = "0.61.3", features = [ - "Win32_Media_Audio_Endpoints", - "Win32_System_Com_StructuredStorage", - "Win32_System_Variant", + "Win32_Media_Audio_Endpoints", + "Win32_System_Com_StructuredStorage", + "Win32_System_Variant", ] } [profile.release] diff --git a/src-tauri/src/helpers/clamshell.rs b/src-tauri/src/helpers/clamshell.rs index 0553031..74847e0 100644 --- a/src-tauri/src/helpers/clamshell.rs +++ b/src-tauri/src/helpers/clamshell.rs @@ -38,9 +38,9 @@ pub fn is_laptop() -> Result { .arg("batt") .output() .map_err(|e| e.to_string())?; - + let stdout = String::from_utf8_lossy(&output.stdout); - + // Check if InternalBattery is present (laptops have batteries, desktops typically don't) Ok(stdout.contains("InternalBattery")) } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 96c72be..e6d85cf 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -9,6 +9,7 @@ mod managers; mod overlay; mod settings; mod shortcut; +mod signal_handle; mod tray; mod utils; @@ -17,6 +18,8 @@ use managers::audio::AudioRecordingManager; use managers::history::HistoryManager; use managers::model::ModelManager; use managers::transcription::TranscriptionManager; +use signal_hook::consts::SIGUSR2; +use signal_hook::iterator::Signals; use std::collections::HashMap; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::{Arc, Mutex}; @@ -119,6 +122,12 @@ fn initialize_core_logic(app_handle: &AppHandle) { // Initialize the shortcuts shortcut::init_shortcuts(app_handle); + #[cfg(unix)] + let signals = Signals::new(&[SIGUSR2]).unwrap(); + // Set up SIGUSR2 signal handler for toggling transcription + #[cfg(unix)] + signal_handle::setup_signal_handler(app_handle.clone(), signals); + // Apply macOS Accessory policy if starting hidden #[cfg(target_os = "macos")] { diff --git a/src-tauri/src/signal_handle.rs b/src-tauri/src/signal_handle.rs new file mode 100644 index 0000000..c4445c9 --- /dev/null +++ b/src-tauri/src/signal_handle.rs @@ -0,0 +1,63 @@ +use crate::actions::ACTION_MAP; +use crate::ManagedToggleState; +use log::{debug, info, warn}; +use std::thread; +use tauri::{AppHandle, Manager}; + +#[cfg(unix)] +use signal_hook::consts::SIGUSR2; +#[cfg(unix)] +use signal_hook::iterator::Signals; + +#[cfg(unix)] +pub fn setup_signal_handler(app_handle: AppHandle, mut signals: Signals) { + let app_handle_for_signal = app_handle.clone(); + + debug!("SIGUSR2 signal handler registered successfully"); + thread::spawn(move || { + debug!("SIGUSR2 signal handler thread started"); + for sig in signals.forever() { + match sig { + SIGUSR2 => { + debug!("Received SIGUSR2 signal (signal number: {sig})"); + + let binding_id = "transcribe"; + let shortcut_string = "SIGUSR2"; + + if let Some(action) = ACTION_MAP.get(binding_id) { + let toggle_state_manager = + app_handle_for_signal.state::(); + + let mut states = match toggle_state_manager.lock() { + Ok(s) => s, + Err(e) => { + warn!("Failed to lock toggle state manager: {e}"); + continue; + } + }; + + let is_currently_active = states + .active_toggles + .entry(binding_id.to_string()) + .or_insert(false); + + if *is_currently_active { + debug!("SIGUSR2: Stopping transcription (currently active)"); + action.stop(&app_handle_for_signal, binding_id, shortcut_string); + *is_currently_active = false; // Update state to inactive + debug!("SIGUSR2: Transcription stopped"); + } else { + debug!("SIGUSR2: Starting transcription (currently inactive)"); + action.start(&app_handle_for_signal, binding_id, shortcut_string); + *is_currently_active = true; // Update state to active + info!("SIGUSR2: Transcription started"); + } + } else { + warn!("No action defined in ACTION_MAP for binding ID '{binding_id}'"); + } + } + _ => unreachable!(), + } + } + }); +}