161 lines
4.7 KiB
Rust
161 lines
4.7 KiB
Rust
use crate::settings;
|
|
|
|
use enigo::Enigo;
|
|
use enigo::Key;
|
|
use enigo::Keyboard;
|
|
use enigo::Settings;
|
|
|
|
use rodio::{Decoder, OutputStream, Sink};
|
|
use std::fs::File;
|
|
use std::io::BufReader;
|
|
use std::thread;
|
|
use tauri::image::Image;
|
|
use tauri::tray::TrayIcon;
|
|
use tauri::AppHandle;
|
|
use tauri::Manager;
|
|
use tauri_plugin_clipboard_manager::ClipboardExt;
|
|
|
|
fn send_paste() -> Result<(), String> {
|
|
// Determine the modifier key based on the OS
|
|
#[cfg(target_os = "macos")]
|
|
let modifier_key = Key::Meta; // Command key on macOS
|
|
#[cfg(not(target_os = "macos"))]
|
|
let modifier_key = Key::Control; // Control key on other systems
|
|
|
|
let mut enigo = Enigo::new(&Settings::default())
|
|
.map_err(|e| format!("Failed to initialize Enigo: {}", e))?;
|
|
|
|
// Press both keys
|
|
enigo
|
|
.key(modifier_key, enigo::Direction::Press)
|
|
.map_err(|e| format!("Failed to press modifier key: {}", e))?;
|
|
enigo
|
|
.key(Key::Unicode('v'), enigo::Direction::Press)
|
|
.map_err(|e| format!("Failed to press V key: {}", e))?;
|
|
|
|
// Release both keys
|
|
enigo
|
|
.key(Key::Unicode('v'), enigo::Direction::Release)
|
|
.map_err(|e| format!("Failed to release V key: {}", e))?;
|
|
enigo
|
|
.key(modifier_key, enigo::Direction::Release)
|
|
.map_err(|e| format!("Failed to release modifier key: {}", e))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn paste(text: String, 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))?;
|
|
|
|
// Add a small delay on Windows to ensure clipboard write is complete
|
|
#[cfg(target_os = "windows")]
|
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
|
send_paste()?;
|
|
|
|
// Add another small delay before restoring clipboard
|
|
#[cfg(target_os = "windows")]
|
|
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 enum TrayIconState {
|
|
Idle,
|
|
Recording,
|
|
}
|
|
|
|
pub fn change_tray_icon(app: &AppHandle, icon: TrayIconState) {
|
|
let tray = app.state::<TrayIcon>();
|
|
|
|
let icon_path = match icon {
|
|
TrayIconState::Idle => "resources/tray_idle.png",
|
|
TrayIconState::Recording => "resources/tray_recording.png",
|
|
};
|
|
|
|
let _ = tray.set_icon(Some(
|
|
Image::from_path(
|
|
app.path()
|
|
.resolve(icon_path, tauri::path::BaseDirectory::Resource)
|
|
.expect("failed to resolve"),
|
|
)
|
|
.expect("failed to set icon"),
|
|
));
|
|
}
|
|
|
|
/// Plays an audio resource from the resources directory.
|
|
/// Checks if audio feedback is enabled in settings before playing.
|
|
pub fn play_sound(app: &AppHandle, resource_path: &str) {
|
|
// Check if audio feedback is enabled
|
|
let settings = settings::get_settings(app);
|
|
if !settings.audio_feedback {
|
|
return;
|
|
}
|
|
|
|
let app_handle = app.clone();
|
|
let resource_path = resource_path.to_string();
|
|
|
|
// Spawn a new thread to play the audio without blocking the main thread
|
|
thread::spawn(move || {
|
|
// Get the path to the audio file in resources
|
|
let audio_path = match app_handle
|
|
.path()
|
|
.resolve(&resource_path, tauri::path::BaseDirectory::Resource)
|
|
{
|
|
Ok(path) => path,
|
|
Err(e) => {
|
|
eprintln!(
|
|
"Failed to resolve audio file path '{}': {}",
|
|
resource_path, e
|
|
);
|
|
return;
|
|
}
|
|
};
|
|
|
|
// Try to play the audio file
|
|
if let Err(e) = play_audio_file(&audio_path) {
|
|
eprintln!("Failed to play sound '{}': {}", resource_path, e);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Convenience function to play the recording start sound
|
|
pub fn play_recording_start_sound(app: &AppHandle) {
|
|
play_sound(app, "resources/rec_start.wav");
|
|
}
|
|
|
|
/// Convenience function to play the recording stop sound
|
|
pub fn play_recording_stop_sound(app: &AppHandle) {
|
|
play_sound(app, "resources/rec_stop.wav");
|
|
}
|
|
|
|
fn play_audio_file(path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
|
|
// Get a output stream handle to the default physical sound device
|
|
let (_stream, stream_handle) = OutputStream::try_default()?;
|
|
|
|
// Load the audio file
|
|
let file = File::open(path)?;
|
|
let buf_reader = BufReader::new(file);
|
|
let source = Decoder::new(buf_reader)?;
|
|
|
|
// Create a sink to play the audio
|
|
let sink = Sink::try_new(&stream_handle)?;
|
|
sink.append(source);
|
|
|
|
// Wait for the audio to finish playing
|
|
sink.sleep_until_end();
|
|
|
|
Ok(())
|
|
}
|