// Windows platform mouse hook management use std::ptr::null_mut; use std::sync::atomic::Ordering; use std::time::Instant; use winapi::{ um::winuser::{ SetWindowsHookExW, UnhookWindowsHookEx, CallNextHookEx, SetWindowPos, WH_MOUSE_LL, HC_ACTION, MSLLHOOKSTRUCT, WM_MOUSEMOVE, WM_RBUTTONDOWN, HWND_TOPMOST, SWP_NOSIZE, SWP_NOMOVE, SWP_NOACTIVATE, }, um::libloaderapi::GetModuleHandleW, shared::minwindef::{WPARAM, LPARAM, LRESULT, UINT}, }; use super::types::*; use super::state::*; use super::cursor::update_cursor_position; // Low-level mouse hook procedure pub unsafe extern "system" fn low_level_mouse_proc(n_code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT { if n_code == HC_ACTION { // Extract mouse position from the hook struct for any mouse event let p_msll_hook_struct = l_param as *const MSLLHOOKSTRUCT; if !p_msll_hook_struct.is_null() { let msll_hook_struct = *p_msll_hook_struct; let current_pos = msll_hook_struct.pt; match w_param as UINT { WM_MOUSEMOVE => { let mut last_update_time_guard = LAST_MOUSE_MOVE_TIME.lock().unwrap(); let now = Instant::now(); if last_update_time_guard.map_or(true, |last_time| now.duration_since(last_time) >= MOUSE_UPDATE_INTERVAL) { // update_cursor_position already contains logic to only update if position actually changed via LAST_CURSOR_POS update_cursor_position(¤t_pos); *last_update_time_guard = Some(now); } }, // Handle right mouse button down - immediately update cursor position and force topmost WM_RBUTTONDOWN => { // Force an immediate update of the cursor position when right-clicking // This ensures the overlay is visible before the menu appears update_cursor_position(¤t_pos); // Force the cursor window to be topmost if let Some(hwnd_wrapper) = *CURSOR_WINDOW.lock().unwrap() { SetWindowPos( hwnd_wrapper.0, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE, // SWP_NOACTIVATE is critical ); } }, // Handle other mouse events if needed _ => { /* Other mouse messages can be handled here if needed */ } } } } // Call the next hook in the chain. // Pass the HHOOK of our own hook, loaded from CURRENT_HOOK_FOR_CALLBACK. CallNextHookEx( CURRENT_HOOK_FOR_CALLBACK.load(Ordering::Relaxed) as _, n_code, w_param, l_param, ) } // Install mouse hook function pub fn install_mouse_hook_if_needed() -> bool { let mut hook_guard = MOUSE_HOOK_HANDLE.lock().unwrap(); if hook_guard.is_none() { unsafe { // GetModuleHandleW(null_mut()) gets the HMODULE for the current process (EXE) // which is correct for a hook procedure within the same process. let h_instance = GetModuleHandleW(null_mut()); if h_instance.is_null() { eprintln!("Failed to get module handle for setting mouse hook: {}", std::io::Error::last_os_error()); return false; } let hook = SetWindowsHookExW( WH_MOUSE_LL, // Hook type: Low-level mouse Some(low_level_mouse_proc), // Pointer to hook procedure h_instance, // HMODULE of the DLL containing the hook proc (or current process) 0 // Thread ID (0 for all threads on current desktop) ); if !hook.is_null() { // Create wrapped handle let sync_hook = SyncHHOOK(hook); *hook_guard = Some(sync_hook); CURRENT_HOOK_FOR_CALLBACK.store(hook as *mut _, Ordering::SeqCst); println!("Low-level mouse hook installed successfully."); return true; } else { eprintln!("Failed to install low-level mouse hook: {}", std::io::Error::last_os_error()); return false; } } } true // Already installed } // Uninstall mouse hook function pub fn uninstall_mouse_hook() { let mut hook_guard = MOUSE_HOOK_HANDLE.lock().unwrap(); if let Some(hook_wrapper) = hook_guard.take() { // .take() removes it from Option and returns it unsafe { // Extract the raw handle using .0 let hook = hook_wrapper.0; if UnhookWindowsHookEx(hook) != 0 { CURRENT_HOOK_FOR_CALLBACK.store(std::ptr::null_mut(), Ordering::SeqCst); println!("Low-level mouse hook uninstalled successfully."); } else { eprintln!("Failed to uninstall low-level mouse hook: {}", std::io::Error::last_os_error()); // Put it back if unhooking failed, though this is unlikely. *hook_guard = Some(hook_wrapper); } } } }