feat(linux): add SIGUSR1 signal for transcribe_with_post_process (#759)

* feat(linux): add SIGUSR1 signal for transcribe_with_post_process

Register SIGUSR1 alongside SIGUSR2 in the signal handler so Linux
users can trigger transcription with post-processing via system
signals (e.g. `pkill -SIGUSR1 -x handy`). This mirrors the existing
SIGUSR2 toggle pattern and maps to the `transcribe_with_post_process`
action already present in ACTION_MAP.

Closes #758

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* readme

* format

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: CJ Pais <cj@cjpais.com>
This commit is contained in:
Ari 2026-02-16 09:41:33 -05:00 committed by GitHub
parent 7fdde63460
commit 203ba1dbc2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 13 deletions

View file

@ -118,10 +118,18 @@ Without these tools, Handy falls back to enigo which may have limited compatibil
- The recording overlay is disabled by default on Linux (`Overlay Position: None`) because certain compositors treat it as the active window. When the overlay is visible it can steal focus, which prevents Handy from pasting back into the application that triggered transcription. If you enable the overlay anyway, be aware that clipboard-based pasting might fail or end up in the wrong window.
- If you are having trouble with the app, running with the environment variable `WEBKIT_DISABLE_DMABUF_RENDERER=1` may help
- You can manage global shortcuts outside of Handy and still control the app via signals. Sending `SIGUSR2` to the Handy process toggles recording on/off, which lets Wayland window managers or other hotkey daemons keep ownership of keybindings. Example (Sway):
- You can manage global shortcuts outside of Handy and still control the app via signals, which lets Wayland window managers or other hotkey daemons keep ownership of keybindings:
| Signal | Action | Example |
| --------- | ----------------------------------------- | ---------------------- |
| `SIGUSR2` | Toggle transcription | `pkill -USR2 -n handy` |
| `SIGUSR1` | Toggle transcription with post-processing | `pkill -USR1 -n handy` |
Example Sway config:
```ini
bindsym $mod+o exec pkill -USR2 -n handy
bindsym $mod+p exec pkill -USR1 -n handy
```
`pkill` here simply delivers the signal—it does not terminate the process.

View file

@ -26,7 +26,7 @@ use managers::history::HistoryManager;
use managers::model::ModelManager;
use managers::transcription::TranscriptionManager;
#[cfg(unix)]
use signal_hook::consts::SIGUSR2;
use signal_hook::consts::{SIGUSR1, SIGUSR2};
#[cfg(unix)]
use signal_hook::iterator::Signals;
use std::sync::atomic::{AtomicU8, Ordering};
@ -132,8 +132,8 @@ fn initialize_core_logic(app_handle: &AppHandle) {
// This matches the pattern used for Enigo initialization.
#[cfg(unix)]
let signals = Signals::new(&[SIGUSR2]).unwrap();
// Set up SIGUSR2 signal handler for toggling transcription
let signals = Signals::new(&[SIGUSR1, SIGUSR2]).unwrap();
// Set up signal handlers for toggling transcription
#[cfg(unix)]
signal_handle::setup_signal_handler(app_handle.clone(), signals);

View file

@ -4,22 +4,25 @@ use std::thread;
use tauri::{AppHandle, Manager};
#[cfg(unix)]
use signal_hook::consts::SIGUSR2;
use signal_hook::consts::{SIGUSR1, SIGUSR2};
#[cfg(unix)]
use signal_hook::iterator::Signals;
#[cfg(unix)]
pub fn setup_signal_handler(app_handle: AppHandle, mut signals: Signals) {
debug!("SIGUSR2 signal handler registered");
debug!("Signal handlers registered (SIGUSR1, SIGUSR2)");
thread::spawn(move || {
for sig in signals.forever() {
if sig == SIGUSR2 {
debug!("Received SIGUSR2");
if let Some(c) = app_handle.try_state::<TranscriptionCoordinator>() {
c.send_input("transcribe", "SIGUSR2", true, false);
} else {
warn!("TranscriptionCoordinator not initialized");
}
let (binding_id, signal_name) = match sig {
SIGUSR1 => ("transcribe_with_post_process", "SIGUSR1"),
SIGUSR2 => ("transcribe", "SIGUSR2"),
_ => continue,
};
debug!("Received {signal_name}");
if let Some(c) = app_handle.try_state::<TranscriptionCoordinator>() {
c.send_input(binding_id, signal_name, true, false);
} else {
warn!("TranscriptionCoordinator not initialized");
}
}
});