From 7016e96673236d3d00c7742fb6a4e18e55e51907 Mon Sep 17 00:00:00 2001 From: JobyGitGud Date: Fri, 30 May 2025 23:20:39 -0500 Subject: [PATCH] Refactor large functions into smaller components --- frontend/src-tauri/src/lib.rs | 113 +++++++------ .../src-tauri/src/platform/windows/cursor.rs | 149 +++++++++++++----- 2 files changed, 177 insertions(+), 85 deletions(-) diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 4cb68e9..3422ff7 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -99,7 +99,7 @@ fn select_cursor( let cursor = &app_state.cursors[index]; - let filename = Path::new(&cursor.path) + let _filename = Path::new(&cursor.path) .file_name() .and_then(|name| name.to_str()) .ok_or_else(|| format!("Invalid cursor path: {}", cursor.path))?; @@ -123,7 +123,7 @@ fn next_cursor( let cursor = &app_state.cursors[app_state.current_index]; - let filename = Path::new(&cursor.path) + let _filename = Path::new(&cursor.path) .file_name() .and_then(|name| name.to_str()) .ok_or_else(|| format!("Invalid cursor path: {}", cursor.path))?; @@ -200,71 +200,90 @@ fn restore_default_cursors() -> Result<(), String> { } } -// Function to load cursor files from src/cursors directory -fn load_cursor_files() -> Vec { - let mut cursors = Vec::new(); - - // Determine the path to src/cursors relative to src-tauri (CARGO_MANIFEST_DIR) +// Get possible cursor directory paths +fn get_cursor_directory_paths() -> Vec { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // Check multiple possible locations - let possible_cursor_dirs = vec![ + vec![ manifest_dir.join("../src/cursors"), manifest_dir.join("src/cursors"), manifest_dir.join("cursors"), manifest_dir.clone(), // Check manifest dir itself - ]; + ] +} + +// Process a single cursor file entry +fn process_cursor_file(entry: std::fs::DirEntry) -> Option { + let path = entry.path(); + + if path.is_file() + && path + .extension() + .map_or(false, |ext| ext == "png" || ext == "cur") + { + let name = path + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("Unknown") + .to_string(); + + Some(Cursor { + name, + path: path.to_string_lossy().to_string(), + }) + } else { + None + } +} + +// Scan cursor directories for cursor files +fn scan_cursor_directories() -> Vec { + let possible_cursor_dirs = get_cursor_directory_paths(); for cursors_dir in possible_cursor_dirs.iter() { if cursors_dir.exists() { if let Ok(entries) = std::fs::read_dir(cursors_dir) { - for entry in entries.filter_map(Result::ok) { - let path = entry.path(); + let cursors: Vec = entries + .filter_map(Result::ok) + .filter_map(process_cursor_file) + .collect(); - if path.is_file() - && path - .extension() - .map_or(false, |ext| ext == "png" || ext == "cur") - { - let name = path - .file_stem() - .and_then(|s| s.to_str()) - .unwrap_or("Unknown") - .to_string(); - - cursors.push(Cursor { - name, - path: path.to_string_lossy().to_string(), - }); - } + if !cursors.is_empty() { + return cursors; } } - - if !cursors.is_empty() { - break; - } } } - // If we found no cursors, use mock data with paths relative to what resolve_resource expects - if cursors.is_empty() { - eprintln!("No cursor files found, using fallback cursors"); + Vec::new() +} - // Mock data paths should reflect their eventual resource path structure - cursors.push(Cursor { +// Create fallback cursors when no files are found +fn create_fallback_cursors() -> Vec { + eprintln!("No cursor files found, using fallback cursors"); + + vec![ + Cursor { name: "Default".to_string(), - path: "src/cursors/default.png".to_string(), // Filename will be 'default.png' - }); - - cursors.push(Cursor { + path: "src/cursors/default.png".to_string(), + }, + Cursor { name: "Icon".to_string(), - path: "src/cursors/icon.png".to_string(), // Filename will be 'icon.png' - }); - - cursors.push(Cursor { + path: "src/cursors/icon.png".to_string(), + }, + Cursor { name: "Red".to_string(), - path: "src/cursors/red.png".to_string(), // Filename will be 'red.png' - }); + path: "src/cursors/red.png".to_string(), + }, + ] +} + +// Function to load cursor files from src/cursors directory +fn load_cursor_files() -> Vec { + let mut cursors = scan_cursor_directories(); + + if cursors.is_empty() { + cursors = create_fallback_cursors(); } // Sort cursors by name for consistent ordering diff --git a/frontend/src-tauri/src/platform/windows/cursor.rs b/frontend/src-tauri/src/platform/windows/cursor.rs index c848c57..349c921 100644 --- a/frontend/src-tauri/src/platform/windows/cursor.rs +++ b/frontend/src-tauri/src/platform/windows/cursor.rs @@ -15,6 +15,7 @@ use winapi::{ }, um::consoleapi::SetConsoleCtrlHandler, shared::minwindef::{TRUE, FALSE, DWORD}, + shared::windef::{HDC, HBITMAP, HGDIOBJ}, }; use ::image::{RgbaImage, open as image_open}; @@ -27,20 +28,48 @@ extern "system" fn ctrl_handler(_ctrl_type: u32) -> i32 { FALSE // Allow other handlers (like default exit) to run } -// Create a cursor from an RGBA image -unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { - let (width, height) = image.dimensions(); - let width_i32 = width as i32; - let height_i32 = height as i32; +// Device context setup result +struct DeviceContexts { + screen_dc: HDC, + color_dc: HDC, + mask_dc: HDC, +} - // Create device context and bitmap for the color data +impl DeviceContexts { + unsafe fn cleanup(&self) { + DeleteDC(self.mask_dc); + DeleteDC(self.color_dc); + ReleaseDC(null_mut(), self.screen_dc); + } +} + +// Bitmap creation result +struct CursorBitmaps { + color_bitmap: HBITMAP, + mask_bitmap: HBITMAP, + color_bits_ptr: *mut winapi::ctypes::c_void, + mask_bits_ptr: *mut winapi::ctypes::c_void, + old_color_bitmap: HGDIOBJ, + old_mask_bitmap: HGDIOBJ, +} + +impl CursorBitmaps { + unsafe fn cleanup(&self, contexts: &DeviceContexts) { + SelectObject(contexts.color_dc, self.old_color_bitmap); + SelectObject(contexts.mask_dc, self.old_mask_bitmap); + DeleteObject(self.color_bitmap as _); + DeleteObject(self.mask_bitmap as _); + } +} + +// Setup device contexts for cursor creation +unsafe fn setup_device_contexts() -> Option { let screen_dc = GetDC(null_mut()); if screen_dc.is_null() { eprintln!("Failed to get screen DC"); return None; } - // Create a compatible DC for our bitmap let color_dc = CreateCompatibleDC(screen_dc); if color_dc.is_null() { eprintln!("Failed to create compatible DC for color"); @@ -48,7 +77,6 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { return None; } - // Create a compatible DC for our mask bitmap let mask_dc = CreateCompatibleDC(screen_dc); if mask_dc.is_null() { eprintln!("Failed to create compatible DC for mask"); @@ -57,11 +85,24 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { return None; } + Some(DeviceContexts { + screen_dc, + color_dc, + mask_dc, + }) +} + +// Create bitmaps for cursor creation +unsafe fn create_cursor_bitmaps( + contexts: &DeviceContexts, + width: i32, + height: i32, +) -> Option { // Set up BITMAPINFO structure for DIB let mut bitmap_info: BITMAPINFO = std::mem::zeroed(); bitmap_info.bmiHeader.biSize = std::mem::size_of::() as u32; - bitmap_info.bmiHeader.biWidth = width_i32; - bitmap_info.bmiHeader.biHeight = -height_i32; // Negative for top-down DIB + bitmap_info.bmiHeader.biWidth = width; + bitmap_info.bmiHeader.biHeight = -height; // Negative for top-down DIB bitmap_info.bmiHeader.biPlanes = 1; bitmap_info.bmiHeader.biBitCount = 32; // 32 bits per pixel (BGRA) bitmap_info.bmiHeader.biCompression = BI_RGB; @@ -70,7 +111,7 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { let mut color_bits_ptr: *mut winapi::ctypes::c_void = null_mut(); let mut ppv_bits_color: *mut winapi::ctypes::c_void = color_bits_ptr; let color_bitmap = CreateDIBSection( - color_dc, + contexts.color_dc, &bitmap_info, DIB_RGB_COLORS, &mut ppv_bits_color, @@ -81,9 +122,7 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { if color_bitmap.is_null() || color_bits_ptr.is_null() { eprintln!("Failed to create color DIB section"); - DeleteDC(mask_dc); - DeleteDC(color_dc); - ReleaseDC(null_mut(), screen_dc); + contexts.cleanup(); return None; } @@ -92,7 +131,7 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { let mut mask_bits_ptr: *mut winapi::ctypes::c_void = null_mut(); let mut ppv_bits: *mut winapi::ctypes::c_void = mask_bits_ptr; let mask_bitmap = CreateDIBSection( - mask_dc, + contexts.mask_dc, &bitmap_info, DIB_RGB_COLORS, &mut ppv_bits, @@ -104,32 +143,51 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { if mask_bitmap.is_null() || mask_bits_ptr.is_null() { eprintln!("Failed to create mask DIB section"); DeleteObject(color_bitmap as _); - DeleteDC(mask_dc); - DeleteDC(color_dc); - ReleaseDC(null_mut(), screen_dc); + contexts.cleanup(); return None; } // Select the bitmaps into the DCs - let old_color_bitmap = SelectObject(color_dc, color_bitmap as _); - let old_mask_bitmap = SelectObject(mask_dc, mask_bitmap as _); + let old_color_bitmap = SelectObject(contexts.color_dc, color_bitmap as _); + let old_mask_bitmap = SelectObject(contexts.mask_dc, mask_bitmap as _); if old_color_bitmap.is_null() || old_mask_bitmap.is_null() { eprintln!("Failed to select bitmaps into DCs"); DeleteObject(mask_bitmap as _); DeleteObject(color_bitmap as _); - DeleteDC(mask_dc); - DeleteDC(color_dc); - ReleaseDC(null_mut(), screen_dc); + contexts.cleanup(); return None; } + Some(CursorBitmaps { + color_bitmap, + mask_bitmap, + color_bits_ptr, + mask_bits_ptr, + old_color_bitmap, + old_mask_bitmap, + }) +} + +// Process image data and populate bitmaps +unsafe fn process_image_data( + image: &RgbaImage, + bitmaps: &CursorBitmaps, + width: u32, + height: u32, +) { // Copy the image data to the color DIB, converting RGBA to BGRA - let color_bits = std::slice::from_raw_parts_mut(color_bits_ptr as *mut u8, (width * height * 4) as usize); + let color_bits = std::slice::from_raw_parts_mut( + bitmaps.color_bits_ptr as *mut u8, + (width * height * 4) as usize, + ); // Calculate mask stride in bytes (1 bit per pixel, padded to DWORD) let mask_stride = ((width + 31) / 32) * 4; - let mask_bits = std::slice::from_raw_parts_mut(mask_bits_ptr as *mut u8, (mask_stride * height) as usize); + let mask_bits = std::slice::from_raw_parts_mut( + bitmaps.mask_bits_ptr as *mut u8, + (mask_stride * height) as usize, + ); // Clear mask bits (all transparent initially) for i in 0..(mask_stride * height) as usize { @@ -165,29 +223,25 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { } } } +} +// Create cursor from prepared bitmaps +unsafe fn create_cursor_from_bitmaps( + bitmaps: &CursorBitmaps, + width: u32, + height: u32, +) -> Option { // Create ICONINFO structure let mut icon_info: ICONINFO = std::mem::zeroed(); icon_info.fIcon = FALSE; // This is a cursor, not an icon icon_info.xHotspot = width as DWORD / 2; // Center hotspot icon_info.yHotspot = height as DWORD / 2; - icon_info.hbmMask = mask_bitmap; - icon_info.hbmColor = color_bitmap; + icon_info.hbmMask = bitmaps.mask_bitmap; + icon_info.hbmColor = bitmaps.color_bitmap; // Create the cursor let cursor = CreateIconIndirect(&mut icon_info as *mut _); - // Clean up resources - SelectObject(color_dc, old_color_bitmap); - SelectObject(mask_dc, old_mask_bitmap); - DeleteDC(color_dc); - DeleteDC(mask_dc); - ReleaseDC(null_mut(), screen_dc); - - // Windows makes a copy of the bitmaps for the cursor, so we can delete them - DeleteObject(color_bitmap as _); - DeleteObject(mask_bitmap as _); - if cursor.is_null() { eprintln!("Failed to create cursor"); return None; @@ -197,6 +251,25 @@ unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { Some(SyncHCURSOR(cursor)) } +// Create a cursor from an RGBA image +unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option { + let (width, height) = image.dimensions(); + let width_i32 = width as i32; + let height_i32 = height as i32; + + let contexts = setup_device_contexts()?; + let bitmaps = create_cursor_bitmaps(&contexts, width_i32, height_i32)?; + + process_image_data(image, &bitmaps, width, height); + + let cursor = create_cursor_from_bitmaps(&bitmaps, width, height); + + bitmaps.cleanup(&contexts); + contexts.cleanup(); + + cursor +} + // Helper function to safely lock a mutex with error handling fn safe_lock<'a, T>(mutex: &'a std::sync::Mutex, operation_name: &str) -> Option> { match mutex.lock() {