Fix cursor swapping issues and minimize cursor flashing

This commit is contained in:
2025-05-11 18:48:04 -05:00
parent b3d85106a8
commit a92ef1e07c
+132 -38
View File
@@ -196,18 +196,49 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option<SyncHCURSOR> {
// Cleanup function to restore cursors and clean up resources
pub fn cleanup() {
println!("Running cursor cleanup...");
// Restore system cursors if they were hidden
if CURSOR_HIDDEN.swap(false, Ordering::SeqCst) { // Use swap to ensure it runs only once
// Use swap to ensure it runs only once
if CURSOR_HIDDEN.swap(false, Ordering::SeqCst) {
println!("Restoring system cursors...");
unsafe {
// Force Windows to reload default cursor settings
SystemParametersInfoW(SPI_SETCURSORS, 0, null_mut(), 0);
// Also try the A version just in case
super::state::reset_system_cursors();
// Try multiple approaches to ensure cursors are restored
// 1. First try to restore original cursors if we saved them
let original_cursors_opt = ORIGINAL_CURSORS.lock().unwrap().take();
if let Some(original_cursors) = original_cursors_opt {
println!("Restoring {} saved original cursors", original_cursors.len());
for (cursor_id, cursor) in original_cursors {
if !cursor.0.is_null() {
// Make a copy of the original cursor
let cursor_copy = CopyIcon(cursor.0);
if !cursor_copy.is_null() {
if SetSystemCursor(cursor_copy, cursor_id) == 0 {
eprintln!("Failed to restore original cursor for ID: {}", cursor_id);
DestroyIcon(cursor_copy);
}
} else {
eprintln!("Failed to copy original cursor for ID: {}", cursor_id);
}
// Reset to default arrow cursor
unsafe {
// Destroy the original cursor we saved
DestroyIcon(cursor.0);
}
}
} else {
println!("No saved original cursors found");
}
// 2. Force Windows to reload default cursor settings
println!("Forcing Windows to reload default cursor settings");
SystemParametersInfoW(SPI_SETCURSORS, 0, null_mut(), 0);
// 3. Also try the A version for maximum compatibility
super::state::reset_system_cursors();
// 4. Reset to default arrow cursor
let arrow_cursor = LoadCursorW(null_mut(), MAKEINTRESOURCEW(IDC_ARROW as u16));
if !arrow_cursor.is_null() {
SetCursor(arrow_cursor);
@@ -222,14 +253,19 @@ pub fn cleanup() {
let custom_cursor_to_destroy = CUSTOM_CURSOR.lock().unwrap().take();
if let Some(cursor_wrapper) = custom_cursor_to_destroy {
unsafe {
if !cursor_wrapper.0.is_null() {
DestroyIcon(cursor_wrapper.0);
println!("Custom cursor destroyed during cleanup.");
}
}
}
// Reset the last known cursor position
*LAST_CURSOR_POS.lock().unwrap() = None;
// Clear the current cursor image
*CURRENT_CURSOR_IMAGE.lock().unwrap() = None;
// Unregister control handler
if CTRL_HANDLER_REGISTERED.load(Ordering::SeqCst) {
unsafe {
@@ -241,6 +277,8 @@ pub fn cleanup() {
}
}
}
println!("Cursor cleanup complete.");
}
// --- Public API Functions ---
@@ -291,6 +329,8 @@ pub fn hide_system_cursor() -> bool {
// Update the cursor image using CursorInfo (with caching)
pub fn update_cursor_info(cursor_info: &mut crate::platform::CursorInfo) -> bool {
println!("Updating cursor to: {}", cursor_info.name);
// Use cached image if available, otherwise load from disk
let image = if let Some(ref cached_image) = cursor_info.image {
println!("Using cached image for cursor: {}", cursor_info.name);
@@ -315,51 +355,58 @@ pub fn update_cursor_info(cursor_info: &mut crate::platform::CursorInfo) -> bool
*CURRENT_CURSOR_IMAGE.lock().unwrap() = Some(image.clone());
unsafe {
// Use cached Windows cursor if available
#[cfg(windows)]
if let Some(cached_cursor) = cursor_info.win_cursor {
println!("Using cached Windows cursor handle for: {}", cursor_info.name);
// Create a new cursor first before destroying the old one
// This minimizes the time when no custom cursor is active
let new_cursor = if let Some(ref cached_image) = cursor_info.image {
// Always create a fresh cursor from the image to avoid invalid handle issues
create_cursor_from_image(cached_image)
} else {
create_cursor_from_image(&image)
};
// Apply the cached cursor to all system cursors
let success = apply_cursor_to_all_system_cursors(cached_cursor.0);
if !success {
eprintln!("Failed to apply cached cursor to all system cursors");
// Even if we failed to set all system cursors, try to at least set the current cursor
SetCursor(cached_cursor.0);
}
if let Some(new_cursor_wrapper) = new_cursor {
// Apply the new cursor to all system cursors first
let success = apply_cursor_to_all_system_cursors(new_cursor_wrapper.0);
return true;
}
// Clean up old cursor if it exists
// Now that the new cursor is applied, we can clean up the old one
let mut cursor_guard = CUSTOM_CURSOR.lock().unwrap();
if let Some(cursor_wrapper) = cursor_guard.take() {
DestroyIcon(cursor_wrapper.0);
if let Some(old_cursor) = cursor_guard.take() {
// Only destroy the old cursor after the new one is applied
DestroyIcon(old_cursor.0);
}
// Create new cursor
if let Some(cursor_wrapper) = create_cursor_from_image(&image) {
// Store the cursor for later use
*cursor_guard = Some(cursor_wrapper);
// Store the new cursor
*cursor_guard = Some(new_cursor_wrapper);
// Cache the Windows cursor handle in the CursorInfo
// Update the cached cursor in CursorInfo
#[cfg(windows)]
{
println!("Caching Windows cursor handle for: {}", cursor_info.name);
cursor_info.win_cursor = Some(cursor_wrapper);
// Always update the cached cursor to the fresh one
cursor_info.win_cursor = Some(new_cursor_wrapper);
}
// Apply the custom cursor to all system cursors
let success = apply_cursor_to_all_system_cursors(cursor_wrapper.0);
if !success {
eprintln!("Failed to apply cursor to all system cursors");
// Even if we failed to set all system cursors, try to at least set the current cursor
SetCursor(cursor_wrapper.0);
SetCursor(new_cursor_wrapper.0);
}
return true;
} else {
eprintln!("Failed to create cursor from image");
// If we failed to create a new cursor, try to use the cached one as a fallback
#[cfg(windows)]
if let Some(cached_cursor) = cursor_info.win_cursor {
if !cached_cursor.0.is_null() {
println!("Falling back to cached cursor as a last resort");
let mut cursor_guard = CUSTOM_CURSOR.lock().unwrap();
*cursor_guard = Some(cached_cursor);
SetCursor(cached_cursor.0);
return true;
}
}
return false;
}
}
@@ -371,24 +418,71 @@ pub fn update_cursor_info(cursor_info: &mut crate::platform::CursorInfo) -> bool
unsafe fn apply_cursor_to_all_system_cursors(cursor: winapi::shared::windef::HCURSOR) -> bool {
let mut success = true;
// First, ensure the cursor is valid
if cursor.is_null() {
eprintln!("Cannot apply null cursor to system cursors");
return false;
}
// Immediately set the current cursor to minimize flashing
SetCursor(cursor);
// Apply the cursor to all system cursor types
for &cursor_id in SYSTEM_CURSORS.iter() {
// Make a copy of the cursor for each system cursor type
// Start with the most commonly used cursors first
let priority_cursors = [
32512, // IDC_ARROW (most important)
32513, // IDC_IBEAM (text cursor)
32649, // IDC_HAND (links)
];
// Apply to priority cursors first
for &cursor_id in priority_cursors.iter() {
// Make a copy of the cursor
let cursor_copy = CopyIcon(cursor);
if cursor_copy.is_null() {
eprintln!("Failed to copy cursor for system cursor ID: {}", cursor_id);
let error = std::io::Error::last_os_error();
eprintln!("Failed to copy cursor for priority ID: {} - Error: {}", cursor_id, error);
success = false;
continue;
}
// Set the system cursor
if SetSystemCursor(cursor_copy, cursor_id) == 0 {
eprintln!("Failed to set system cursor for ID: {}", cursor_id);
let error = std::io::Error::last_os_error();
eprintln!("Failed to set cursor for priority ID: {} - Error: {}", cursor_id, error);
DestroyIcon(cursor_copy); // Clean up the copy if we failed to set it
success = false;
}
}
// Now apply to the remaining cursors
for &cursor_id in SYSTEM_CURSORS.iter() {
// Skip the priority cursors we already processed
if priority_cursors.contains(&cursor_id) {
continue;
}
// Make a copy of the cursor
let cursor_copy = CopyIcon(cursor);
if cursor_copy.is_null() {
// Don't log errors for non-priority cursors to reduce console spam
success = false;
continue;
}
// Set the system cursor
if SetSystemCursor(cursor_copy, cursor_id) == 0 {
// Don't log errors for non-priority cursors to reduce console spam
DestroyIcon(cursor_copy); // Clean up the copy if we failed to set it
success = false;
}
}
// If we had any failures, set the current cursor again as a fallback
if !success {
SetCursor(cursor);
}
success
}