use serde::Serialize; use tauri::{App, AppHandle, Manager}; use tauri_plugin_global_shortcut::GlobalShortcutExt; use tauri_plugin_global_shortcut::{Shortcut, ShortcutState}; use crate::actions::ACTION_MAP; use crate::settings::ShortcutBinding; use crate::settings::{self, get_settings}; use crate::ManagedToggleState; pub fn init_shortcuts(app: &App) { let settings = settings::load_or_create_app_settings(app); // Register shortcuts with the bindings from settings for (_id, binding) in settings.bindings { // Pass app.handle() which is &AppHandle if let Err(e) = _register_shortcut(app.handle(), binding) { eprintln!("Failed to register shortcut {}: {}", _id, e); } } } #[derive(Serialize)] pub struct BindingResponse { success: bool, binding: Option, error: Option, } #[tauri::command] pub fn change_binding( app: AppHandle, id: String, binding: String, ) -> Result { let mut settings = settings::get_settings(&app); // Get the binding to modify let binding_to_modify = match settings.bindings.get(&id) { Some(binding) => binding.clone(), None => { return Ok(BindingResponse { success: false, binding: None, error: Some(format!("Binding with id '{}' not found", id)), }) } }; // Unregister the existing binding if let Err(e) = _unregister_shortcut(&app, binding_to_modify.clone()) { return Ok(BindingResponse { success: false, binding: None, error: Some(format!("Failed to unregister shortcut: {}", e)), }); } // Create an updated binding let mut updated_binding = binding_to_modify; updated_binding.current_binding = binding; // Register the new binding if let Err(e) = _register_shortcut(&app, updated_binding.clone()) { return Ok(BindingResponse { success: false, binding: None, error: Some(format!("Failed to register shortcut: {}", e)), }); } // Update the binding in the settings settings.bindings.insert(id, updated_binding.clone()); // Save the settings settings::write_settings(&app, settings); // Return the updated binding Ok(BindingResponse { success: true, binding: Some(updated_binding), error: None, }) } #[tauri::command] pub fn reset_binding(app: AppHandle, id: String) -> Result { let binding = settings::get_stored_binding(&app, &id); return change_binding(app, id, binding.default_binding); } #[tauri::command] pub fn change_ptt_setting(app: AppHandle, enabled: bool) -> Result<(), String> { let mut settings = settings::get_settings(&app); // TODO if the setting is currently false, we probably want to // cancel any ongoing recordings or actions settings.push_to_talk = enabled; settings::write_settings(&app, settings); Ok(()) } #[tauri::command] pub fn change_audio_feedback_setting(app: AppHandle, enabled: bool) -> Result<(), String> { let mut settings = settings::get_settings(&app); settings.audio_feedback = enabled; settings::write_settings(&app, settings); Ok(()) } #[tauri::command] pub fn change_translate_to_english_setting(app: AppHandle, enabled: bool) -> Result<(), String> { let mut settings = settings::get_settings(&app); settings.translate_to_english = enabled; settings::write_settings(&app, settings); Ok(()) } #[tauri::command] pub fn change_selected_language_setting(app: AppHandle, language: String) -> Result<(), String> { let mut settings = settings::get_settings(&app); settings.selected_language = language; settings::write_settings(&app, settings); Ok(()) } #[tauri::command] pub fn change_show_overlay_setting(app: AppHandle, enabled: bool) -> Result<(), String> { let mut settings = settings::get_settings(&app); settings.show_overlay = enabled; settings::write_settings(&app, settings); Ok(()) } fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), String> { // Parse shortcut and return error if it fails let shortcut = match binding.current_binding.parse::() { Ok(s) => s, Err(e) => return Err(format!("Failed to parse shortcut: {}", e)), }; // Clone binding.id for use in the closure let binding_id_for_closure = binding.id.clone(); app.global_shortcut() .on_shortcut(shortcut, move |ah, scut, event| { if scut == &shortcut { let shortcut_string = scut.into_string(); let settings = get_settings(ah); if let Some(action) = ACTION_MAP.get(&binding_id_for_closure) { if settings.push_to_talk { if event.state == ShortcutState::Pressed { action.start(ah, &binding_id_for_closure, &shortcut_string); } else if event.state == ShortcutState::Released { action.stop(ah, &binding_id_for_closure, &shortcut_string); } } else { if event.state == ShortcutState::Pressed { let toggle_state_manager = ah.state::(); let mut states = toggle_state_manager.lock().expect("Failed to lock toggle state manager"); let is_currently_active = states.active_toggles .entry(binding_id_for_closure.clone()) .or_insert(false); if *is_currently_active { action.stop( ah, &binding_id_for_closure, &shortcut_string, ); *is_currently_active = false; // Update state to inactive } else { action.start(ah, &binding_id_for_closure, &shortcut_string); *is_currently_active = true; // Update state to active } } } } else { println!( "Warning: No action defined in ACTION_MAP for shortcut ID '{}'. Shortcut: '{}', State: {:?}", binding_id_for_closure, shortcut_string, event.state ); } } }) .map_err(|e| format!("Couldn't register shortcut: {}", e))?; Ok(()) } fn _unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), String> { let shortcut = match binding.current_binding.parse::() { Ok(s) => s, Err(e) => return Err(format!("Failed to parse shortcut: {}", e)), }; app.global_shortcut() .unregister(shortcut) .map_err(|e| format!("Failed to unregister shortcut: {}", e))?; Ok(()) }