use std::sync::Mutex; use serde::{Serialize, Deserialize}; use tauri::{Manager, AppHandle}; use std::path::{Path, PathBuf}; use std::env; mod platform; // Define cursor structure #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Cursor { name: String, path: String, } // App state to track cursors pub struct AppState { cursors: Vec, current_index: usize, } // Thread-safe app state wrapper pub struct AppStateWrapper(Mutex); // Command to get all cursors #[tauri::command] fn get_cursors(state: tauri::State) -> Vec { let app_state = state.0.lock().unwrap(); app_state.cursors.clone() } // Command to get current cursor index #[tauri::command] fn get_current_cursor_index(state: tauri::State) -> usize { let app_state = state.0.lock().unwrap(); app_state.current_index } // Command to select a cursor #[tauri::command] fn select_cursor(index: usize, state: tauri::State, app_handle: AppHandle) -> Result<(), String> { println!("=== RESOURCE DEBUG [select_cursor] ==="); let mut app_state = state.0.lock().unwrap(); if index < app_state.cursors.len() { app_state.current_index = index; let cursor = &app_state.cursors[index]; println!("Cursor selected: {}", cursor.name); println!("Original path: {}", cursor.path); // Get the resource directory let resource_dir = app_handle.path_resolver().resource_dir(); println!("Resource dir: {:?}", resource_dir); // List contents of resource dir if let Some(res_dir) = &resource_dir { println!("Listing contents of resource dir: {}", res_dir.display()); if let Ok(entries) = std::fs::read_dir(res_dir) { for (i, entry) in entries.filter_map(Result::ok).enumerate() { println!(" {}: {}", i, entry.path().display()); // List subdirectories if entry.path().is_dir() { if let Ok(subentries) = std::fs::read_dir(entry.path()) { for (j, subentry) in subentries.filter_map(Result::ok).enumerate() { println!(" {}.{}: {}", i, j, subentry.path().display()); } } } } } else { println!("Failed to read resource directory"); } } let filename = Path::new(&cursor.path) .file_name() .and_then(|name| name.to_str()) .ok_or_else(|| format!("Invalid cursor path: {}", cursor.path))?; println!("Extracted filename: {}", filename); // Try using the actual file path directly println!("Using direct file path: {}", cursor.path); // Verify the file exists if Path::new(&cursor.path).exists() { println!("File exists, applying cursor..."); if let Err(e) = platform::set_system_cursor(&cursor.path) { println!("Error applying cursor: {}", e); return Err(format!("Failed to apply cursor '{}': {}", cursor.name, e)); } // Emit an event to notify the frontend let _ = app_handle.emit_all("cursor_changed", index); // Ignore error println!("Successfully applied cursor using actual file path"); return Ok(()); } else { println!("File doesn't exist: {}", cursor.path); return Err(format!("Cursor file does not exist: {}", cursor.path)); } } else { Err("Index out of bounds".to_string()) } } // Command to go to the next cursor #[tauri::command] fn next_cursor(state: tauri::State, app_handle: AppHandle) -> Result { println!("=== RESOURCE DEBUG [next_cursor] ==="); let mut app_state = state.0.lock().unwrap(); if !app_state.cursors.is_empty() { app_state.current_index = (app_state.current_index + 1) % app_state.cursors.len(); let cursor = &app_state.cursors[app_state.current_index]; println!("Next cursor: {} ({})", cursor.name, 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))?; // Try both resource paths like in select_cursor println!("Extracted filename: {}", filename); // Try using the actual file path directly println!("Using direct file path: {}", cursor.path); // Verify the file exists if Path::new(&cursor.path).exists() { println!("File exists, applying cursor..."); if let Err(e) = platform::set_system_cursor(&cursor.path) { println!("Error applying cursor: {}", e); return Err(format!("Failed to apply cursor '{}': {}", cursor.name, e)); } // Emit an event to notify the frontend let _ = app_handle.emit_all("cursor_changed", app_state.current_index); // Ignore error println!("Successfully applied cursor using actual file path"); return Ok(app_state.current_index); } else { println!("File doesn't exist: {}", cursor.path); return Err(format!("Cursor file does not exist: {}", cursor.path)); } } else { Err("No cursors available".to_string()) } } // Command to quit the application #[tauri::command] fn quit_app(app_handle: AppHandle) { // Clean up and restore default cursor before exit match platform::restore_system_cursor() { Ok(_) => println!("Default cursor restored"), Err(e) => eprintln!("Failed to restore default cursor: {}", e), } // Exit the application std::thread::spawn(move || { app_handle.exit(0); }); } // Function to load cursor files from src/cursors directory fn load_cursor_files() -> Vec { println!("=== RESOURCE DEBUG [load_cursor_files] ==="); let mut cursors = Vec::new(); // Determine the path to src/cursors relative to src-tauri (CARGO_MANIFEST_DIR) let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); println!("CARGO_MANIFEST_DIR: {}", manifest_dir.display()); // Check multiple possible locations let possible_cursor_dirs = vec![ manifest_dir.join("../src/cursors"), manifest_dir.join("src/cursors"), manifest_dir.join("cursors"), manifest_dir.clone(), // Check manifest dir itself ]; println!("CHECKING FOR PNG/CUR FILES IN MULTIPLE DIRECTORIES:"); for (i, cursors_dir) in possible_cursor_dirs.iter().enumerate() { println!("Checking dir {}: {}", i+1, cursors_dir.display()); println!(" Directory exists: {}", cursors_dir.exists()); if cursors_dir.exists() { println!(" Looking for cursor files in this directory..."); if let Ok(entries) = std::fs::read_dir(cursors_dir) { for entry in entries.filter_map(Result::ok) { let path = entry.path(); println!(" Found: {}", path.display()); 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(); println!(" ADDING CURSOR: {} at path {}", name, path.display()); cursors.push(Cursor { name, path: path.to_string_lossy().to_string(), }); } } } if !cursors.is_empty() { println!(" Found {} cursor files in this directory.", cursors.len()); break; } } } // If we found no cursors, use mock data with paths relative to what resolve_resource expects if cursors.is_empty() { println!("No cursors found in any directory. Using mock data instead."); println!("IMPORTANT: This is probably the source of your problem!"); println!("The app could not find your cursor PNG files. Check that they exist in one of these directories:"); for dir in &possible_cursor_dirs { println!(" {}", dir.display()); } // Mock data paths should reflect their eventual resource path structure cursors.push(Cursor { name: "Default".to_string(), path: "src/cursors/default.png".to_string(), // Filename will be 'default.png' }); cursors.push(Cursor { name: "Icon".to_string(), path: "src/cursors/icon.png".to_string(), // Filename will be 'icon.png' }); cursors.push(Cursor { name: "Red".to_string(), path: "src/cursors/red.png".to_string(), // Filename will be 'red.png' }); println!("Created {} mock cursor entries", cursors.len()); } println!("Loaded {} cursor files", cursors.len()); // Sort cursors by name for consistent ordering cursors.sort_by(|a, b| a.name.cmp(&b.name)); cursors } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { println!("Starting Cursor Rush application"); // Initialize platform-specific cursor handling if let Err(e) = platform::initialize() { eprintln!("Failed to initialize platform: {}", e); } // Load cursors from assets directory let cursors = load_cursor_files(); // Create app state let app_state = AppState { cursors, current_index: 0, }; // This is one of the simplest ways to initialize Tauri tauri::Builder::default() .manage(AppStateWrapper(Mutex::new(app_state))) .invoke_handler(tauri::generate_handler![ get_cursors, get_current_cursor_index, select_cursor, next_cursor, quit_app ]) .setup(|_app| { // Register for events or perform additional setup println!("Tauri application setup complete"); Ok(()) }) // This matches exactly how it was before our changes .run(tauri::generate_context!()) .expect("error while running tauri application"); }