Refactor large functions into smaller components

This commit is contained in:
2025-05-30 23:20:39 -05:00
parent 09374eecb3
commit 7016e96673
2 changed files with 177 additions and 85 deletions
+51 -32
View File
@@ -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,25 +200,20 @@ fn restore_default_cursors() -> Result<(), String> {
}
}
// Function to load cursor files from src/cursors directory
fn load_cursor_files() -> Vec<Cursor> {
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<PathBuf> {
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
];
]
}
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) {
// Process a single cursor file entry
fn process_cursor_file(entry: std::fs::DirEntry) -> Option<Cursor> {
let path = entry.path();
if path.is_file()
@@ -232,39 +227,63 @@ fn load_cursor_files() -> Vec<Cursor> {
.unwrap_or("Unknown")
.to_string();
cursors.push(Cursor {
Some(Cursor {
name,
path: path.to_string_lossy().to_string(),
});
}
})
} else {
None
}
}
// Scan cursor directories for cursor files
fn scan_cursor_directories() -> Vec<Cursor> {
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) {
let cursors: Vec<Cursor> = entries
.filter_map(Result::ok)
.filter_map(process_cursor_file)
.collect();
if !cursors.is_empty() {
break;
return cursors;
}
}
}
}
// If we found no cursors, use mock data with paths relative to what resolve_resource expects
if cursors.is_empty() {
Vec::new()
}
// Create fallback cursors when no files are found
fn create_fallback_cursors() -> Vec<Cursor> {
eprintln!("No cursor files found, using fallback cursors");
// Mock data paths should reflect their eventual resource path structure
cursors.push(Cursor {
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<Cursor> {
let mut cursors = scan_cursor_directories();
if cursors.is_empty() {
cursors = create_fallback_cursors();
}
// Sort cursors by name for consistent ordering
+111 -38
View File
@@ -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<SyncHCURSOR> {
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<DeviceContexts> {
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<SyncHCURSOR> {
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<SyncHCURSOR> {
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<CursorBitmaps> {
// Set up BITMAPINFO structure for DIB
let mut bitmap_info: BITMAPINFO = std::mem::zeroed();
bitmap_info.bmiHeader.biSize = std::mem::size_of::<BITMAPINFOHEADER>() 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<SyncHCURSOR> {
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<SyncHCURSOR> {
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<SyncHCURSOR> {
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<SyncHCURSOR> {
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<SyncHCURSOR> {
}
}
}
}
// Create cursor from prepared bitmaps
unsafe fn create_cursor_from_bitmaps(
bitmaps: &CursorBitmaps,
width: u32,
height: u32,
) -> Option<SyncHCURSOR> {
// 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<SyncHCURSOR> {
Some(SyncHCURSOR(cursor))
}
// Create a cursor from an RGBA image
unsafe fn create_cursor_from_image(image: &RgbaImage) -> Option<SyncHCURSOR> {
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<T>, operation_name: &str) -> Option<std::sync::MutexGuard<'a, T>> {
match mutex.lock() {