Handy/src-tauri/src/utils.rs
Datenfalke 0afd5ec01f
feat(linux): add KDE Plasma 6 Wayland support via kwtype (#676)
* feat(linux): add KDE Plasma 6 Wayland support via kwtype

On KDE Plasma 6 Wayland, existing input tools have limitations:
- ydotool type: cannot handle German umlauts (ö, ä, ü, ß)
- wtype: doesn't work (KDE lacks zwp_virtual_keyboard_manager_v1)
- ydotool key: key combos don't reach native Wayland apps like Kate

This adds support for kwtype, which uses the KDE Fake Input protocol:
- Properly handles Unicode characters including German umlauts
- Works with native Wayland applications (Kate, Dolphin, etc.)
- Requires kwtype to be installed: https://github.com/Sporif/KWtype

Changes:
- Add is_kde_plasma() and is_kde_wayland() detection in utils.rs
- Add kwtype integration for direct text typing on KDE Wayland
- Add wl-copy integration for clipboard operations on Wayland
- Skip wtype on KDE (known not to work)

Recommended: Use PasteMethod::Direct on KDE Wayland for best results.

* add -- to make sure input is good

---------

Co-authored-by: CJ Pais <cj@cjpais.com>
2026-02-05 19:11:33 +08:00

69 lines
2.4 KiB
Rust

use crate::managers::audio::AudioRecordingManager;
use crate::managers::transcription::TranscriptionManager;
use crate::shortcut;
use crate::ManagedToggleState;
use log::{info, warn};
use std::sync::Arc;
use tauri::{AppHandle, Manager};
// Re-export all utility modules for easy access
// pub use crate::audio_feedback::*;
pub use crate::clipboard::*;
pub use crate::overlay::*;
pub use crate::tray::*;
/// Centralized cancellation function that can be called from anywhere in the app.
/// Handles cancelling both recording and transcription operations and updates UI state.
pub fn cancel_current_operation(app: &AppHandle) {
info!("Initiating operation cancellation...");
// Unregister the cancel shortcut asynchronously
shortcut::unregister_cancel_shortcut(app);
// First, reset all shortcut toggle states.
// This is critical for non-push-to-talk mode where shortcuts toggle on/off
let toggle_state_manager = app.state::<ManagedToggleState>();
if let Ok(mut states) = toggle_state_manager.lock() {
states.active_toggles.values_mut().for_each(|v| *v = false);
} else {
warn!("Failed to lock toggle state manager during cancellation");
}
// Cancel any ongoing recording
let audio_manager = app.state::<Arc<AudioRecordingManager>>();
audio_manager.cancel_recording();
// Update tray icon and hide overlay
change_tray_icon(app, crate::tray::TrayIconState::Idle);
hide_recording_overlay(app);
// Unload model if immediate unload is enabled
let tm = app.state::<Arc<TranscriptionManager>>();
tm.maybe_unload_immediately("cancellation");
info!("Operation cancellation completed - returned to idle state");
}
/// Check if using the Wayland display server protocol
#[cfg(target_os = "linux")]
pub fn is_wayland() -> bool {
std::env::var("WAYLAND_DISPLAY").is_ok()
|| std::env::var("XDG_SESSION_TYPE")
.map(|v| v.to_lowercase() == "wayland")
.unwrap_or(false)
}
/// Check if running on KDE Plasma desktop environment
#[cfg(target_os = "linux")]
pub fn is_kde_plasma() -> bool {
std::env::var("XDG_CURRENT_DESKTOP")
.map(|v| v.to_uppercase().contains("KDE"))
.unwrap_or(false)
|| std::env::var("KDE_SESSION_VERSION").is_ok()
}
/// Check if running on KDE Plasma with Wayland
#[cfg(target_os = "linux")]
pub fn is_kde_wayland() -> bool {
is_wayland() && is_kde_plasma()
}