diff --git a/src-tauri/src/clipboard_manager.rs b/src-tauri/src/clipboard_manager.rs index 8200d73..aa1e3ce 100644 --- a/src-tauri/src/clipboard_manager.rs +++ b/src-tauri/src/clipboard_manager.rs @@ -359,261 +359,17 @@ impl ClipboardManager { } // Simulate Ctrl+V to paste - simulate_paste()?; + crate::input_simulator::simulate_paste_keystroke()?; Ok(()) } } -/// Simulate Ctrl+V keypress for paste injection -#[cfg(target_os = "linux")] -fn simulate_paste() -> Result<(), String> { - // Longer delay to ensure focus is properly restored and clipboard is ready - std::thread::sleep(std::time::Duration::from_millis(10)); - eprintln!("[SimulatePaste] Sending Ctrl+V..."); - // Try uinput first - works for ALL apps (X11, XWayland, native Wayland) - match simulate_paste_uinput() { - Ok(()) => { - eprintln!("[SimulatePaste] Ctrl+V sent via uinput"); - return Ok(()); - } - Err(e) => { - eprintln!("[SimulatePaste] uinput failed: {}, trying fallbacks...", e); - } - } - // Fallback to enigo for XWayland apps - match simulate_paste_enigo() { - Ok(()) => { - eprintln!("[SimulatePaste] Ctrl+V sent via enigo"); - return Ok(()); - } - Err(e) => { - eprintln!("[SimulatePaste] enigo failed: {}", e); - } - } - // Last fallback to xdotool - if std::env::var("DISPLAY").is_ok() { - if let Ok(output) = std::process::Command::new("xdotool") - .args(["key", "--clearmodifiers", "ctrl+v"]) - .output() - { - if output.status.success() { - eprintln!("[SimulatePaste] Ctrl+V sent via xdotool"); - return Ok(()); - } - } - } - Err("All paste methods failed".to_string()) -} - -/// Simulate paste using uinput (works for ALL apps including native Wayland) -#[cfg(target_os = "linux")] -fn simulate_paste_uinput() -> Result<(), String> { - use std::fs::OpenOptions; - use std::io::Write; - use std::os::unix::io::AsRawFd; - - // Linux input event codes - const EV_SYN: u16 = 0x00; - const EV_KEY: u16 = 0x01; - const SYN_REPORT: u16 = 0x00; - const KEY_LEFTCTRL: u16 = 29; - const KEY_V: u16 = 47; - - // input_event struct layout for x86_64: - // struct timeval { long tv_sec; long tv_usec; } = 16 bytes - // __u16 type = 2 bytes - // __u16 code = 2 bytes - // __s32 value = 4 bytes - // Total = 24 bytes - - fn make_event(type_: u16, code: u16, value: i32) -> [u8; 24] { - let mut event = [0u8; 24]; - // timeval (16 bytes) - leave as zeros - // type (2 bytes at offset 16) - event[16..18].copy_from_slice(&type_.to_ne_bytes()); - // code (2 bytes at offset 18) - event[18..20].copy_from_slice(&code.to_ne_bytes()); - // value (4 bytes at offset 20) - event[20..24].copy_from_slice(&value.to_ne_bytes()); - event - } - - // Open uinput device - let mut uinput = OpenOptions::new() - .write(true) - .open("/dev/uinput") - .map_err(|e| format!("Failed to open /dev/uinput: {}", e))?; - - // Set up uinput device - // UI_SET_EVBIT = 0x40045564 - // UI_SET_KEYBIT = 0x40045565 - const UI_SET_EVBIT: libc::c_ulong = 0x40045564; - const UI_SET_KEYBIT: libc::c_ulong = 0x40045565; - const UI_DEV_SETUP: libc::c_ulong = 0x405c5503; - const UI_DEV_CREATE: libc::c_ulong = 0x5501; - const UI_DEV_DESTROY: libc::c_ulong = 0x5502; - - unsafe { - // Enable EV_KEY events - if libc::ioctl(uinput.as_raw_fd(), UI_SET_EVBIT, EV_KEY as libc::c_int) < 0 { - return Err("Failed to set EV_KEY".to_string()); - } - - // Enable the keys we need - if libc::ioctl( - uinput.as_raw_fd(), - UI_SET_KEYBIT, - KEY_LEFTCTRL as libc::c_int, - ) < 0 - { - return Err("Failed to set KEY_LEFTCTRL".to_string()); - } - if libc::ioctl(uinput.as_raw_fd(), UI_SET_KEYBIT, KEY_V as libc::c_int) < 0 { - return Err("Failed to set KEY_V".to_string()); - } - - // Setup device info - #[repr(C)] - struct UinputSetup { - id: [u16; 4], // bus, vendor, product, version - name: [u8; 80], - ff_effects_max: u32, - } - - let mut setup = UinputSetup { - id: [0x03, 0x1234, 0x5678, 0x0001], // BUS_USB - name: [0; 80], - ff_effects_max: 0, - }; - let name = b"clipboard-paste-helper"; - setup.name[..name.len()].copy_from_slice(name); - - if libc::ioctl(uinput.as_raw_fd(), UI_DEV_SETUP, &setup) < 0 { - return Err("Failed to setup uinput device".to_string()); - } - - // Create the device - if libc::ioctl(uinput.as_raw_fd(), UI_DEV_CREATE) < 0 { - return Err("Failed to create uinput device".to_string()); - } - } - - // Longer delay for device to be fully ready and recognized by the system - std::thread::sleep(std::time::Duration::from_millis(100)); - - // Send Ctrl+V with proper timing - // Press Ctrl first and wait for it to register - uinput - .write_all(&make_event(EV_KEY, KEY_LEFTCTRL, 1)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - - // Wait for Ctrl to be fully registered - std::thread::sleep(std::time::Duration::from_millis(30)); - - // Press V while Ctrl is held - uinput - .write_all(&make_event(EV_KEY, KEY_V, 1)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - - std::thread::sleep(std::time::Duration::from_millis(30)); - - // Release V - uinput - .write_all(&make_event(EV_KEY, KEY_V, 0)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - - std::thread::sleep(std::time::Duration::from_millis(30)); - - // Release Ctrl last - uinput - .write_all(&make_event(EV_KEY, KEY_LEFTCTRL, 0)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - - // Wait for events to be processed before destroying device - std::thread::sleep(std::time::Duration::from_millis(100)); - - // Destroy the device - unsafe { - libc::ioctl(uinput.as_raw_fd(), UI_DEV_DESTROY); - } - - Ok(()) -} - -/// Fallback paste simulation using enigo (X11/XWayland only) -#[cfg(target_os = "linux")] -fn simulate_paste_enigo() -> Result<(), String> { - use enigo::{Direction, Enigo, Key, Keyboard, Settings}; - - let mut enigo = Enigo::new(&Settings::default()).map_err(|e| { - eprintln!("[SimulatePaste] Failed to create Enigo: {}", e); - e.to_string() - })?; - - // Press Ctrl - enigo.key(Key::Control, Direction::Press).map_err(|e| { - eprintln!("[SimulatePaste] Ctrl press failed: {}", e); - e.to_string() - })?; - - std::thread::sleep(std::time::Duration::from_millis(20)); - - // Press and release V - enigo - .key(Key::Unicode('v'), Direction::Press) - .map_err(|e| { - eprintln!("[SimulatePaste] V press failed: {}", e); - e.to_string() - })?; - - std::thread::sleep(std::time::Duration::from_millis(20)); - - enigo - .key(Key::Unicode('v'), Direction::Release) - .map_err(|e| { - eprintln!("[SimulatePaste] V release failed: {}", e); - e.to_string() - })?; - - std::thread::sleep(std::time::Duration::from_millis(20)); - - // Release Ctrl - enigo.key(Key::Control, Direction::Release).map_err(|e| { - eprintln!("[SimulatePaste] Ctrl release failed: {}", e); - e.to_string() - })?; - - eprintln!("[SimulatePaste] Ctrl+V sent via enigo"); - Ok(()) -} - -#[cfg(not(target_os = "linux"))] -fn simulate_paste() -> Result<(), String> { - // Fallback for other platforms - just set clipboard - Ok(()) -} impl Default for ClipboardManager { fn default() -> Self { diff --git a/src-tauri/src/input_simulator.rs b/src-tauri/src/input_simulator.rs new file mode 100644 index 0000000..b11eb4c --- /dev/null +++ b/src-tauri/src/input_simulator.rs @@ -0,0 +1,177 @@ +#[cfg(target_os = "linux")] +pub fn simulate_paste_keystroke() -> Result<(), String> { + // Small delay before paste + std::thread::sleep(std::time::Duration::from_millis(10)); + + eprintln!("[SimulatePaste] Sending Ctrl+V..."); + + // Try uinput first + if let Ok(()) = simulate_paste_uinput() { + eprintln!("[SimulatePaste] Ctrl+V sent via uinput"); + return Ok(()); + } + + // Fallback to enigo + if let Ok(()) = simulate_paste_enigo() { + eprintln!("[SimulatePaste] Ctrl+V sent via enigo"); + return Ok(()); + } + + // Last fallback to xdotool + if std::env::var("DISPLAY").is_ok() { + if let Ok(output) = std::process::Command::new("xdotool") + .args(["key", "--clearmodifiers", "ctrl+v"]) + .output() + { + if output.status.success() { + eprintln!("[SimulatePaste] Ctrl+V sent via xdotool"); + return Ok(()); + } + } + } + + Err("All paste methods failed".to_string()) +} + +#[cfg(not(target_os = "linux"))] +pub fn simulate_paste_keystroke() -> Result<(), String> { + Ok(()) +} + +#[cfg(target_os = "linux")] +fn simulate_paste_uinput() -> Result<(), String> { + use std::fs::OpenOptions; + use std::io::Write; + use std::os::unix::io::AsRawFd; + + const EV_SYN: u16 = 0x00; + const EV_KEY: u16 = 0x01; + const SYN_REPORT: u16 = 0x00; + const KEY_LEFTCTRL: u16 = 29; + const KEY_V: u16 = 47; + + fn make_event(type_: u16, code: u16, value: i32) -> [u8; 24] { + let mut event = [0u8; 24]; + event[16..18].copy_from_slice(&type_.to_ne_bytes()); + event[18..20].copy_from_slice(&code.to_ne_bytes()); + event[20..24].copy_from_slice(&value.to_ne_bytes()); + event + } + + let mut uinput = OpenOptions::new() + .write(true) + .open("/dev/uinput") + .map_err(|e| format!("Failed to open /dev/uinput: {}", e))?; + + const UI_SET_EVBIT: libc::c_ulong = 0x40045564; + const UI_SET_KEYBIT: libc::c_ulong = 0x40045565; + const UI_DEV_SETUP: libc::c_ulong = 0x405c5503; + const UI_DEV_CREATE: libc::c_ulong = 0x5501; + const UI_DEV_DESTROY: libc::c_ulong = 0x5502; + + unsafe { + if libc::ioctl(uinput.as_raw_fd(), UI_SET_EVBIT, EV_KEY as libc::c_int) < 0 { + return Err("Failed to set EV_KEY".to_string()); + } + if libc::ioctl( + uinput.as_raw_fd(), + UI_SET_KEYBIT, + KEY_LEFTCTRL as libc::c_int, + ) < 0 + { + return Err("Failed to set KEY_LEFTCTRL".to_string()); + } + if libc::ioctl(uinput.as_raw_fd(), UI_SET_KEYBIT, KEY_V as libc::c_int) < 0 { + return Err("Failed to set KEY_V".to_string()); + } + + #[repr(C)] + struct UinputSetup { + id: [u16; 4], + name: [u8; 80], + ff_effects_max: u32, + } + + let mut setup = UinputSetup { + id: [0x03, 0x1234, 0x5678, 0x0001], + name: [0; 80], + ff_effects_max: 0, + }; + let name = b"emoji-paste-helper"; + setup.name[..name.len()].copy_from_slice(name); + + if libc::ioctl(uinput.as_raw_fd(), UI_DEV_SETUP, &setup) < 0 { + return Err("Failed to setup uinput device".to_string()); + } + if libc::ioctl(uinput.as_raw_fd(), UI_DEV_CREATE) < 0 { + return Err("Failed to create uinput device".to_string()); + } + } + + std::thread::sleep(std::time::Duration::from_millis(100)); + + // Press Ctrl + uinput + .write_all(&make_event(EV_KEY, KEY_LEFTCTRL, 1)) + .map_err(|e| e.to_string())?; + uinput + .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) + .map_err(|e| e.to_string())?; + uinput.flush().map_err(|e| e.to_string())?; + std::thread::sleep(std::time::Duration::from_millis(30)); + + // Press V + uinput + .write_all(&make_event(EV_KEY, KEY_V, 1)) + .map_err(|e| e.to_string())?; + uinput + .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) + .map_err(|e| e.to_string())?; + uinput.flush().map_err(|e| e.to_string())?; + std::thread::sleep(std::time::Duration::from_millis(30)); + + // Release V + uinput + .write_all(&make_event(EV_KEY, KEY_V, 0)) + .map_err(|e| e.to_string())?; + uinput + .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) + .map_err(|e| e.to_string())?; + uinput.flush().map_err(|e| e.to_string())?; + std::thread::sleep(std::time::Duration::from_millis(30)); + + // Release Ctrl + uinput + .write_all(&make_event(EV_KEY, KEY_LEFTCTRL, 0)) + .map_err(|e| e.to_string())?; + uinput + .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) + .map_err(|e| e.to_string())?; + uinput.flush().map_err(|e| e.to_string())?; + std::thread::sleep(std::time::Duration::from_millis(100)); + + unsafe { + libc::ioctl(uinput.as_raw_fd(), UI_DEV_DESTROY); + } + + Ok(()) +} + +#[cfg(target_os = "linux")] +fn simulate_paste_enigo() -> Result<(), String> { + use enigo::{Direction, Enigo, Key, Keyboard, Settings}; + + let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?; + + enigo + .key(Key::Control, Direction::Press) + .map_err(|e| e.to_string())?; + enigo + .key(Key::Unicode('v'), Direction::Click) + .map_err(|e| e.to_string())?; + enigo + .key(Key::Control, Direction::Release) + .map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3e9ed4d..ee5f07d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,6 +5,7 @@ pub mod clipboard_manager; pub mod emoji_manager; pub mod focus_manager; pub mod hotkey_manager; +pub mod input_simulator; pub use clipboard_manager::{ClipboardContent, ClipboardItem, ClipboardManager}; pub use emoji_manager::{EmojiManager, EmojiUsage}; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 8f68e71..5a33b30 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -13,6 +13,7 @@ use win11_clipboard_history_lib::clipboard_manager::{ClipboardItem, ClipboardMan use win11_clipboard_history_lib::emoji_manager::{EmojiManager, EmojiUsage}; use win11_clipboard_history_lib::focus_manager::{restore_focused_window, save_focused_window}; use win11_clipboard_history_lib::hotkey_manager::{HotkeyAction, HotkeyManager}; +use win11_clipboard_history_lib::input_simulator::simulate_paste_keystroke; /// Application state shared across all handlers pub struct AppState { @@ -84,8 +85,49 @@ fn get_recent_emojis(state: State) -> Vec { state.emoji_manager.lock().get_recent() } +/// Helper to paste text via clipboard pipeline +/// Pipeline: Mark as pasted -> Write to clipboard -> Hide window -> Restore focus -> Simulate Ctrl+V +async fn paste_text_via_clipboard( + app: &AppHandle, + state: &State<'_, AppState>, + text: &str, +) -> Result<(), String> { + // Step 1: Mark text as "pasted by us" so clipboard watcher ignores it + { + let mut clipboard_manager = state.clipboard_manager.lock(); + clipboard_manager.mark_text_as_pasted(text); + } + + // Step 2: Write to system clipboard (transport only) + { + use arboard::Clipboard; + let mut clipboard = Clipboard::new().map_err(|e| format!("Clipboard error: {}", e))?; + clipboard + .set_text(text) + .map_err(|e| format!("Failed to set clipboard: {}", e))?; + } + + // Step 3: Hide window + if let Some(window) = app.get_webview_window("main") { + let _ = window.hide(); + } + + // Step 4: Restore focus + if let Err(e) = restore_focused_window() { + eprintln!("Warning: Failed to restore focus: {}", e); + } + + // Step 5: Wait for focus to be fully restored + tokio::time::sleep(std::time::Duration::from_millis(150)).await; + + // Step 6: Simulate Ctrl+V + simulate_paste_keystroke()?; + + Ok(()) +} + /// Paste an emoji character -/// Pipeline: Write to clipboard -> Hide window -> Restore focus -> Simulate Ctrl+V +/// Pipeline: Record usage -> Delegate to paste_text_via_clipboard #[tauri::command] async fn paste_emoji( app: AppHandle, @@ -94,238 +136,19 @@ async fn paste_emoji( ) -> Result<(), String> { eprintln!("[PasteEmoji] Starting paste for emoji: {}", char); - // Step 1: Record usage in LRU cache (for "recently used" section in emoji picker) + // Record usage in LRU cache { let mut emoji_manager = state.emoji_manager.lock(); emoji_manager.record_usage(&char); } - // Step 2: Mark this emoji as "pasted by us" so clipboard watcher ignores it - { - let mut clipboard_manager = state.clipboard_manager.lock(); - clipboard_manager.mark_text_as_pasted(&char); - } - - // Step 3: Write emoji to system clipboard (transport only, not history) - { - use arboard::Clipboard; - let mut clipboard = Clipboard::new().map_err(|e| format!("Clipboard error: {}", e))?; - clipboard - .set_text(&char) - .map_err(|e| format!("Failed to set clipboard: {}", e))?; - eprintln!("[PasteEmoji] Emoji written to clipboard"); - } - - // Step 4: Hide our window - if let Some(window) = app.get_webview_window("main") { - let _ = window.hide(); - eprintln!("[PasteEmoji] Window hidden"); - } - - // Step 5: Wait and restore focus to previous window - if let Err(e) = restore_focused_window() { - eprintln!("[PasteEmoji] Warning: Failed to restore focus: {}", e); - } - - // Step 6: Wait for focus to be fully restored - tokio::time::sleep(std::time::Duration::from_millis(150)).await; - eprintln!("[PasteEmoji] Focus restored, simulating paste..."); - - // Step 7: Simulate Ctrl+V to paste the emoji - simulate_paste_keystroke()?; + // Delegate to shared pipeline + paste_text_via_clipboard(&app, &state, &char).await?; eprintln!("[PasteEmoji] Paste complete"); Ok(()) } -/// Simulate Ctrl+V keystroke (extracted from clipboard_manager for reuse) -#[cfg(target_os = "linux")] -fn simulate_paste_keystroke() -> Result<(), String> { - // Small delay before paste - std::thread::sleep(std::time::Duration::from_millis(10)); - - eprintln!("[SimulatePaste] Sending Ctrl+V..."); - - // Try uinput first - if let Ok(()) = simulate_paste_uinput() { - eprintln!("[SimulatePaste] Ctrl+V sent via uinput"); - return Ok(()); - } - - // Fallback to enigo - if let Ok(()) = simulate_paste_enigo() { - eprintln!("[SimulatePaste] Ctrl+V sent via enigo"); - return Ok(()); - } - - // Last fallback to xdotool - if std::env::var("DISPLAY").is_ok() { - if let Ok(output) = std::process::Command::new("xdotool") - .args(["key", "--clearmodifiers", "ctrl+v"]) - .output() - { - if output.status.success() { - eprintln!("[SimulatePaste] Ctrl+V sent via xdotool"); - return Ok(()); - } - } - } - - Err("All paste methods failed".to_string()) -} - -#[cfg(target_os = "linux")] -fn simulate_paste_uinput() -> Result<(), String> { - use std::fs::OpenOptions; - use std::io::Write; - use std::os::unix::io::AsRawFd; - - const EV_SYN: u16 = 0x00; - const EV_KEY: u16 = 0x01; - const SYN_REPORT: u16 = 0x00; - const KEY_LEFTCTRL: u16 = 29; - const KEY_V: u16 = 47; - - fn make_event(type_: u16, code: u16, value: i32) -> [u8; 24] { - let mut event = [0u8; 24]; - event[16..18].copy_from_slice(&type_.to_ne_bytes()); - event[18..20].copy_from_slice(&code.to_ne_bytes()); - event[20..24].copy_from_slice(&value.to_ne_bytes()); - event - } - - let mut uinput = OpenOptions::new() - .write(true) - .open("/dev/uinput") - .map_err(|e| format!("Failed to open /dev/uinput: {}", e))?; - - const UI_SET_EVBIT: libc::c_ulong = 0x40045564; - const UI_SET_KEYBIT: libc::c_ulong = 0x40045565; - const UI_DEV_SETUP: libc::c_ulong = 0x405c5503; - const UI_DEV_CREATE: libc::c_ulong = 0x5501; - const UI_DEV_DESTROY: libc::c_ulong = 0x5502; - - unsafe { - if libc::ioctl(uinput.as_raw_fd(), UI_SET_EVBIT, EV_KEY as libc::c_int) < 0 { - return Err("Failed to set EV_KEY".to_string()); - } - if libc::ioctl( - uinput.as_raw_fd(), - UI_SET_KEYBIT, - KEY_LEFTCTRL as libc::c_int, - ) < 0 - { - return Err("Failed to set KEY_LEFTCTRL".to_string()); - } - if libc::ioctl(uinput.as_raw_fd(), UI_SET_KEYBIT, KEY_V as libc::c_int) < 0 { - return Err("Failed to set KEY_V".to_string()); - } - - #[repr(C)] - struct UinputSetup { - id: [u16; 4], - name: [u8; 80], - ff_effects_max: u32, - } - - let mut setup = UinputSetup { - id: [0x03, 0x1234, 0x5678, 0x0001], - name: [0; 80], - ff_effects_max: 0, - }; - let name = b"emoji-paste-helper"; - setup.name[..name.len()].copy_from_slice(name); - - if libc::ioctl(uinput.as_raw_fd(), UI_DEV_SETUP, &setup) < 0 { - return Err("Failed to setup uinput device".to_string()); - } - if libc::ioctl(uinput.as_raw_fd(), UI_DEV_CREATE) < 0 { - return Err("Failed to create uinput device".to_string()); - } - } - - std::thread::sleep(std::time::Duration::from_millis(100)); - - // Press Ctrl - uinput - .write_all(&make_event(EV_KEY, KEY_LEFTCTRL, 1)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(30)); - - // Press V - uinput - .write_all(&make_event(EV_KEY, KEY_V, 1)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(30)); - - // Release V - uinput - .write_all(&make_event(EV_KEY, KEY_V, 0)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(30)); - - // Release Ctrl - uinput - .write_all(&make_event(EV_KEY, KEY_LEFTCTRL, 0)) - .map_err(|e| e.to_string())?; - uinput - .write_all(&make_event(EV_SYN, SYN_REPORT, 0)) - .map_err(|e| e.to_string())?; - uinput.flush().map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(100)); - - unsafe { - libc::ioctl(uinput.as_raw_fd(), UI_DEV_DESTROY); - } - - Ok(()) -} - -#[cfg(target_os = "linux")] -fn simulate_paste_enigo() -> Result<(), String> { - use enigo::{Direction, Enigo, Key, Keyboard, Settings}; - - let mut enigo = Enigo::new(&Settings::default()).map_err(|e| e.to_string())?; - - enigo - .key(Key::Control, Direction::Press) - .map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(20)); - - enigo - .key(Key::Unicode('v'), Direction::Press) - .map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(20)); - - enigo - .key(Key::Unicode('v'), Direction::Release) - .map_err(|e| e.to_string())?; - std::thread::sleep(std::time::Duration::from_millis(20)); - - enigo - .key(Key::Control, Direction::Release) - .map_err(|e| e.to_string())?; - - Ok(()) -} - -#[cfg(not(target_os = "linux"))] -fn simulate_paste_keystroke() -> Result<(), String> { - Ok(()) -} - /// Show the clipboard window at cursor position fn show_window_at_cursor(window: &WebviewWindow) { use tauri::{PhysicalPosition, PhysicalSize};