diff --git a/src/app/state.rs b/src/app/state.rs index 631f7ff..fe1891d 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -76,17 +76,36 @@ pub fn update_app(state: &mut CursorRush, message: Message) -> Task { if let Some(shared) = &state.shared_state { if let Ok(mut shared_state) = shared.lock() { shared_state.running = false; + println!("Set running flag to false in shared state"); + } else { + println!("Failed to lock shared state to set running flag"); } + } else { + println!("Shared state not available when handling Quit message"); } + // Unregister hotkeys if possible + if let Some(_) = &state.hotkey_manager { + println!("Unregistering hotkeys before exit"); + // We don't have a list of registered hotkeys, so we'll just drop the manager + // The hotkeys will be unregistered when the manager is dropped + println!("Hotkey manager will be dropped during exit"); + } else { + println!("No hotkey manager available to unregister hotkeys"); + } + // Restore the system cursor before exiting platform::restore_cursor(); + println!("System cursor restored"); // Cleanly drop the tray icon *before* exiting iced // This removes the icon from the system tray immediately if let Some(tray) = state.tray_icon.take() { + println!("Dropping tray icon..."); drop(tray); println!("Tray icon explicitly dropped."); + } else { + println!("No tray icon to drop"); } // Use iced's exit mechanism for a cleaner shutdown @@ -213,9 +232,10 @@ pub struct CursorRush { pub shared_state: Option>>, pub main_window: Option, // Store the main window ID pub tray_icon: Option, // Store the TrayIcon object to keep it alive + pub hotkey_manager: Option, // Store the hotkey manager to keep it alive } -// Make sure the default impl initializes tray_icon to None +// Make sure the default impl initializes tray_icon and hotkey_manager to None impl Default for CursorRush { fn default() -> Self { Self { @@ -224,6 +244,7 @@ impl Default for CursorRush { shared_state: None, main_window: None, tray_icon: None, // Initialize as None + hotkey_manager: None, // Initialize as None } } } @@ -240,6 +261,22 @@ pub fn app_subscription(_state: &CursorRush) -> Subscription { // Return TrayEvent which is essentially a no-op Message::TrayEvent }, + // Add a fallback keyboard handler for Ctrl+Shift+F12 + iced::Event::Keyboard(iced::keyboard::Event::KeyPressed { + key, + modifiers, + .. + }) => { + // Check if Ctrl+Shift+F12 is pressed + if key == iced::keyboard::Key::Named(iced::keyboard::key::Named::F12) && + modifiers.control() && + modifiers.shift() { + println!("Detected Ctrl+Shift+F12 via iced keyboard event"); + Message::Quit + } else { + Message::TrayEvent // No-op for other key events + } + }, // Handle other events _ => Message::TrayEvent, // Use TrayEvent as a no-op message } @@ -326,20 +363,38 @@ pub fn boot_app() -> (CursorRush, Task) { eprintln!("Failed to initialize tray icon, application might not function correctly in background."); } - setup_hotkeys(shared_state.clone()); + // Set up hotkeys and store the manager in the application state + state.hotkey_manager = setup_hotkeys(shared_state.clone()); + if state.hotkey_manager.is_none() { + eprintln!("Failed to initialize hotkey manager, hotkeys will not work!"); + } else { + println!("Hotkey manager successfully initialized and stored in application state"); + } (state, Task::none()) } // Set up global hotkeys -fn setup_hotkeys(shared_state: Arc>) { +fn setup_hotkeys(shared_state: Arc>) -> Option { // Set up global hotkey manager let hotkey_manager = match GlobalHotKeyManager::new() { - Ok(manager) => manager, + Ok(manager) => { + println!("Successfully created hotkey manager"); + manager + }, Err(e) => { eprintln!("Failed to create hotkey manager: {}", e); // Continue without hotkey support - GlobalHotKeyManager::new().expect("Critical failure: could not create hotkey manager") + match GlobalHotKeyManager::new() { + Ok(manager) => { + println!("Second attempt to create hotkey manager succeeded"); + manager + }, + Err(e) => { + eprintln!("Critical failure: could not create hotkey manager on second attempt: {}", e); + return None; + } + } } }; @@ -351,10 +406,13 @@ fn setup_hotkeys(shared_state: Arc>) { Code::F12, ); + println!("Attempting to register Ctrl+Shift+F12 as exit hotkey"); + // Register the hotkey with better error handling - match hotkey_manager.register(hotkey) { + let registered_hotkey = match hotkey_manager.register(hotkey) { Ok(_) => { println!("Successfully registered Ctrl+Shift+F12 as exit hotkey"); + hotkey }, Err(e) => { eprintln!("Failed to register hotkey: {}", e); @@ -364,21 +422,50 @@ fn setup_hotkeys(shared_state: Arc>) { Code::F12, ); + println!("Attempting to register fallback hotkey Ctrl+Alt+F12"); match hotkey_manager.register(fallback_hotkey) { - Ok(_) => println!("Registered fallback hotkey Ctrl+Alt+F12 instead"), - Err(e) => eprintln!("Failed to register fallback hotkey: {}", e), + Ok(_) => { + println!("Registered fallback hotkey Ctrl+Alt+F12 instead"); + fallback_hotkey + }, + Err(e) => { + eprintln!("Failed to register fallback hotkey: {}", e); + + // Try one more fallback with just F12 + let last_fallback = GlobalHotKey::new(None, Code::F12); + println!("Attempting to register last fallback hotkey F12"); + match hotkey_manager.register(last_fallback) { + Ok(_) => { + println!("Registered last fallback hotkey F12"); + last_fallback + }, + Err(e) => { + eprintln!("Failed to register any hotkey: {}", e); + return None; + } + } + } } } - } + }; + println!("Hotkey registered: {:?}", registered_hotkey); println!("Press Ctrl+Shift+F12 to exit the application"); + // We can't clone the hotkey manager, so we'll use Arc to share it + let manager_arc = Arc::new(hotkey_manager); + let manager_for_thread = Arc::clone(&manager_arc); + // Spawn a thread to listen for hotkeys let state_clone = shared_state.clone(); thread::spawn(move || { println!("Hotkey listener thread started"); let event_receiver = global_hotkey::GlobalHotKeyEvent::receiver(); + // Keep a reference to the manager in this thread to ensure it stays alive + let _manager_ref = &manager_for_thread; + println!("Hotkey manager reference maintained in thread"); + loop { match event_receiver.try_recv() { Ok(event) => { @@ -388,15 +475,20 @@ fn setup_hotkeys(shared_state: Arc>) { // Send Quit message instead of direct exit // Get the global sender if let Some(tx) = TRAY_SENDER.lock().unwrap().as_ref() { + println!("Found global sender, sending Quit message"); if let Err(e) = tx.unbounded_send(Message::Quit) { eprintln!("Failed to send Quit message from hotkey thread: {}", e); // Fallback to direct exit if channel fails + println!("Using fallback exit mechanism"); platform::restore_cursor(); std::process::exit(0); + } else { + println!("Successfully sent Quit message through channel"); } } else { eprintln!("Hotkey: Global sender not initialized yet"); // Fallback to direct exit if sender not available + println!("Global sender not available, using direct exit"); platform::restore_cursor(); std::process::exit(0); } @@ -417,4 +509,15 @@ fn setup_hotkeys(shared_state: Arc>) { } } }); + + // Return the hotkey manager from the Arc so it can be stored in the application state + match Arc::try_unwrap(manager_arc) { + Ok(manager) => Some(manager), + Err(_arc) => { + // If we can't unwrap the Arc (which shouldn't happen), extract a clone from it + println!("Warning: Could not unwrap Arc, using a new instance"); + // Create a new instance as a fallback + GlobalHotKeyManager::new().ok() + } + } } diff --git a/src/integrations/steam.rs b/src/integrations/steam.rs index 0f30977..f1d3843 100644 --- a/src/integrations/steam.rs +++ b/src/integrations/steam.rs @@ -17,6 +17,7 @@ impl Steam { Self { client, single } } + #[allow(dead_code)] pub fn drop_item(&self) { unsafe { let mut r = 0;