* windows paste shift+ins instead of ctrl+v Shift+Insert is older and more universal than Ctrl+V on windows. For example SSH terminal software often will not permit ctrl+v. * add option for shift+ins on windows/linux --------- Co-authored-by: CJ Pais <cj@cjpais.com>
160 lines
5.6 KiB
Rust
160 lines
5.6 KiB
Rust
use crate::settings::{get_settings, ClipboardHandling, PasteMethod};
|
|
use enigo::Enigo;
|
|
use enigo::Key;
|
|
use enigo::Keyboard;
|
|
use enigo::Settings;
|
|
use tauri::AppHandle;
|
|
use tauri_plugin_clipboard_manager::ClipboardExt;
|
|
|
|
/// Sends a Ctrl+V or Cmd+V paste command using platform-specific virtual key codes.
|
|
/// This ensures the paste works regardless of keyboard layout (e.g., Russian, AZERTY, DVORAK).
|
|
fn send_paste_ctrl_v() -> Result<(), String> {
|
|
// Platform-specific key definitions
|
|
#[cfg(target_os = "macos")]
|
|
let (modifier_key, v_key_code) = (Key::Meta, Key::Other(9));
|
|
#[cfg(target_os = "windows")]
|
|
let (modifier_key, v_key_code) = (Key::Control, Key::Other(0x56)); // VK_V
|
|
#[cfg(target_os = "linux")]
|
|
let (modifier_key, v_key_code) = (Key::Control, Key::Unicode('v'));
|
|
|
|
let mut enigo = Enigo::new(&Settings::default())
|
|
.map_err(|e| format!("Failed to initialize Enigo: {}", e))?;
|
|
|
|
// Press modifier + V
|
|
enigo
|
|
.key(modifier_key, enigo::Direction::Press)
|
|
.map_err(|e| format!("Failed to press modifier key: {}", e))?;
|
|
enigo
|
|
.key(v_key_code, enigo::Direction::Click)
|
|
.map_err(|e| format!("Failed to click V key: {}", e))?;
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
|
|
enigo
|
|
.key(modifier_key, enigo::Direction::Release)
|
|
.map_err(|e| format!("Failed to release modifier key: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Sends a Shift+Insert paste command (Windows and Linux only).
|
|
/// This is more universal for terminal applications and legacy software.
|
|
#[cfg(not(target_os = "macos"))]
|
|
fn send_paste_shift_insert() -> Result<(), String> {
|
|
#[cfg(target_os = "windows")]
|
|
let insert_key_code = Key::Other(0x2D); // VK_INSERT
|
|
#[cfg(target_os = "linux")]
|
|
let insert_key_code = Key::Other(0x76); // XK_Insert (keycode 118 / 0x76)
|
|
|
|
let mut enigo = Enigo::new(&Settings::default())
|
|
.map_err(|e| format!("Failed to initialize Enigo: {}", e))?;
|
|
|
|
// Press Shift + Insert
|
|
enigo
|
|
.key(Key::Shift, enigo::Direction::Press)
|
|
.map_err(|e| format!("Failed to press Shift key: {}", e))?;
|
|
enigo
|
|
.key(insert_key_code, enigo::Direction::Click)
|
|
.map_err(|e| format!("Failed to click Insert key: {}", e))?;
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
|
|
enigo
|
|
.key(Key::Shift, enigo::Direction::Release)
|
|
.map_err(|e| format!("Failed to release Shift key: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Pastes text directly using the enigo text method.
|
|
/// This tries to use system input methods if possible, otherwise simulates keystrokes one by one.
|
|
fn paste_via_direct_input(text: &str) -> Result<(), String> {
|
|
let mut enigo = Enigo::new(&Settings::default())
|
|
.map_err(|e| format!("Failed to initialize Enigo: {}", e))?;
|
|
|
|
enigo
|
|
.text(text)
|
|
.map_err(|e| format!("Failed to send text directly: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Pastes text using the clipboard method with Ctrl+V/Cmd+V.
|
|
/// Saves the current clipboard, writes the text, sends paste command, then restores the clipboard.
|
|
fn paste_via_clipboard_ctrl_v(text: &str, app_handle: &AppHandle) -> Result<(), String> {
|
|
let clipboard = app_handle.clipboard();
|
|
|
|
// get the current clipboard content
|
|
let clipboard_content = clipboard.read_text().unwrap_or_default();
|
|
|
|
clipboard
|
|
.write_text(text)
|
|
.map_err(|e| format!("Failed to write to clipboard: {}", e))?;
|
|
|
|
// small delay to ensure the clipboard content has been written to
|
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
|
send_paste_ctrl_v()?;
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
|
// restore the clipboard
|
|
clipboard
|
|
.write_text(&clipboard_content)
|
|
.map_err(|e| format!("Failed to restore clipboard: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Pastes text using the clipboard method with Shift+Insert (Windows/Linux only).
|
|
/// Saves the current clipboard, writes the text, sends paste command, then restores the clipboard.
|
|
#[cfg(not(target_os = "macos"))]
|
|
fn paste_via_clipboard_shift_insert(text: &str, app_handle: &AppHandle) -> Result<(), String> {
|
|
let clipboard = app_handle.clipboard();
|
|
|
|
// get the current clipboard content
|
|
let clipboard_content = clipboard.read_text().unwrap_or_default();
|
|
|
|
clipboard
|
|
.write_text(text)
|
|
.map_err(|e| format!("Failed to write to clipboard: {}", e))?;
|
|
|
|
// small delay to ensure the clipboard content has been written to
|
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
|
send_paste_shift_insert()?;
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
|
// restore the clipboard
|
|
clipboard
|
|
.write_text(&clipboard_content)
|
|
.map_err(|e| format!("Failed to restore clipboard: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
|
let settings = get_settings(&app_handle);
|
|
let paste_method = settings.paste_method;
|
|
|
|
println!("Using paste method: {:?}", paste_method);
|
|
|
|
// Perform the paste operation
|
|
match paste_method {
|
|
PasteMethod::CtrlV => paste_via_clipboard_ctrl_v(&text, &app_handle)?,
|
|
PasteMethod::Direct => paste_via_direct_input(&text)?,
|
|
#[cfg(not(target_os = "macos"))]
|
|
PasteMethod::ShiftInsert => paste_via_clipboard_shift_insert(&text, &app_handle)?,
|
|
}
|
|
|
|
// After pasting, optionally copy to clipboard based on settings
|
|
if settings.clipboard_handling == ClipboardHandling::CopyToClipboard {
|
|
let clipboard = app_handle.clipboard();
|
|
clipboard
|
|
.write_text(&text)
|
|
.map_err(|e| format!("Failed to copy to clipboard: {}", e))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|