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>
This commit is contained in:
parent
7703d10f5f
commit
0afd5ec01f
2 changed files with 107 additions and 9 deletions
|
|
@ -7,7 +7,7 @@ use tauri::{AppHandle, Manager};
|
|||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::utils::is_wayland;
|
||||
use crate::utils::{is_kde_wayland, is_wayland};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::process::Command;
|
||||
|
||||
|
|
@ -23,9 +23,23 @@ fn paste_via_clipboard(
|
|||
let clipboard_content = clipboard.read_text().unwrap_or_default();
|
||||
|
||||
// Write text to clipboard first
|
||||
clipboard
|
||||
// On Wayland, prefer wl-copy for better compatibility (especially with umlauts)
|
||||
#[cfg(target_os = "linux")]
|
||||
let write_result = if is_wayland() && is_wl_copy_available() {
|
||||
info!("Using wl-copy for clipboard write on Wayland");
|
||||
write_clipboard_via_wl_copy(text)
|
||||
} else {
|
||||
clipboard
|
||||
.write_text(text)
|
||||
.map_err(|e| format!("Failed to write to clipboard: {}", e))
|
||||
};
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let write_result = clipboard
|
||||
.write_text(text)
|
||||
.map_err(|e| format!("Failed to write to clipboard: {}", e))?;
|
||||
.map_err(|e| format!("Failed to write to clipboard: {}", e));
|
||||
|
||||
write_result?;
|
||||
|
||||
std::thread::sleep(Duration::from_millis(paste_delay_ms));
|
||||
|
||||
|
|
@ -49,9 +63,16 @@ fn paste_via_clipboard(
|
|||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
|
||||
// Restore original clipboard content
|
||||
clipboard
|
||||
.write_text(&clipboard_content)
|
||||
.map_err(|e| format!("Failed to restore clipboard: {}", e))?;
|
||||
// On Wayland, prefer wl-copy for better compatibility
|
||||
#[cfg(target_os = "linux")]
|
||||
if is_wayland() && is_wl_copy_available() {
|
||||
let _ = write_clipboard_via_wl_copy(&clipboard_content);
|
||||
} else {
|
||||
let _ = clipboard.write_text(&clipboard_content);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let _ = clipboard.write_text(&clipboard_content);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -61,8 +82,9 @@ fn paste_via_clipboard(
|
|||
#[cfg(target_os = "linux")]
|
||||
fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result<bool, String> {
|
||||
if is_wayland() {
|
||||
// Wayland: prefer wtype, then dotool, then ydotool
|
||||
if is_wtype_available() {
|
||||
// Wayland: prefer wtype (but not on KDE), then dotool, then ydotool
|
||||
// Note: wtype doesn't work on KDE (no zwp_virtual_keyboard_manager_v1 support)
|
||||
if !is_kde_wayland() && is_wtype_available() {
|
||||
info!("Using wtype for key combo");
|
||||
send_key_combo_via_wtype(paste_method)?;
|
||||
return Ok(true);
|
||||
|
|
@ -99,8 +121,15 @@ fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result<bool, String>
|
|||
#[cfg(target_os = "linux")]
|
||||
fn try_direct_typing_linux(text: &str) -> Result<bool, String> {
|
||||
if is_wayland() {
|
||||
// KDE Wayland: prefer kwtype (uses KDE Fake Input protocol, supports umlauts)
|
||||
if is_kde_wayland() && is_kwtype_available() {
|
||||
info!("Using kwtype for direct text input on KDE Wayland");
|
||||
type_text_via_kwtype(text)?;
|
||||
return Ok(true);
|
||||
}
|
||||
// Wayland: prefer wtype, then dotool, then ydotool
|
||||
if is_wtype_available() {
|
||||
// Note: wtype doesn't work on KDE (no zwp_virtual_keyboard_manager_v1 support)
|
||||
if !is_kde_wayland() && is_wtype_available() {
|
||||
info!("Using wtype for direct text input");
|
||||
type_text_via_wtype(text)?;
|
||||
return Ok(true);
|
||||
|
|
@ -171,6 +200,26 @@ fn is_xdotool_available() -> bool {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if kwtype is available (KDE Wayland virtual keyboard input tool)
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_kwtype_available() -> bool {
|
||||
Command::new("which")
|
||||
.arg("kwtype")
|
||||
.output()
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if wl-copy is available (Wayland clipboard tool)
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_wl_copy_available() -> bool {
|
||||
Command::new("which")
|
||||
.arg("wl-copy")
|
||||
.output()
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Type text directly via wtype on Wayland.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn type_text_via_wtype(text: &str) -> Result<(), String> {
|
||||
|
|
@ -254,6 +303,40 @@ fn type_text_via_ydotool(text: &str) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Type text directly via kwtype (KDE Wayland virtual keyboard, uses KDE Fake Input protocol).
|
||||
#[cfg(target_os = "linux")]
|
||||
fn type_text_via_kwtype(text: &str) -> Result<(), String> {
|
||||
let output = Command::new("kwtype")
|
||||
.arg("--")
|
||||
.arg(text)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute kwtype: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("kwtype failed: {}", stderr));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write text to clipboard via wl-copy (Wayland clipboard tool).
|
||||
#[cfg(target_os = "linux")]
|
||||
fn write_clipboard_via_wl_copy(text: &str) -> Result<(), String> {
|
||||
let output = Command::new("wl-copy")
|
||||
.arg("--")
|
||||
.arg(text)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute wl-copy: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("wl-copy failed: {}", stderr));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a key combination (e.g., Ctrl+V) via wtype on Wayland.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn send_key_combo_via_wtype(paste_method: &PasteMethod) -> Result<(), String> {
|
||||
|
|
|
|||
|
|
@ -52,3 +52,18 @@ pub fn is_wayland() -> bool {
|
|||
.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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue