Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

198 lines
7.6 KiB
Rust
Raw Normal View History

2025-07-15 18:29:42 +01:00
/// Multi-platform file opening handler
///
/// This module provides unified file opening support across platforms:
/// - macOS: Uses native NSApplication delegate (proper Apple Events)
/// - Windows/Linux: Uses command line arguments (fallback approach)
/// - All platforms: Runtime event handling via Tauri events
use crate::utils::add_log;
use crate::commands::set_opened_file;
2025-07-15 19:37:57 +01:00
use tauri::AppHandle;
2025-07-15 18:29:42 +01:00
/// Initialize file handling for the current platform
2025-07-15 18:49:03 +01:00
pub fn initialize_file_handler(app: &AppHandle<tauri::Wry>) {
2025-07-15 18:29:42 +01:00
add_log("🔧 Initializing file handler...".to_string());
// Platform-specific initialization
#[cfg(target_os = "macos")]
{
add_log("🍎 Using macOS native file handler".to_string());
macos_native::register_open_file_handler(app);
}
#[cfg(not(target_os = "macos"))]
{
add_log("🖥️ Using command line argument file handler".to_string());
let _ = app; // Suppress unused variable warning
}
// Universal: Check command line arguments (works on all platforms)
check_command_line_args();
}
2025-07-16 10:52:38 +01:00
/// Early initialization for macOS delegate registration
pub fn early_init() {
#[cfg(target_os = "macos")]
{
add_log("🔄 Early macOS initialization...".to_string());
macos_native::register_delegate_early();
}
}
2025-07-15 18:29:42 +01:00
/// Check command line arguments for file paths (universal fallback)
fn check_command_line_args() {
let args: Vec<String> = std::env::args().collect();
add_log(format!("🔍 DEBUG: All command line args: {:?}", args));
// Check command line arguments for file opening
for (i, arg) in args.iter().enumerate() {
add_log(format!("🔍 DEBUG: Arg {}: {}", i, arg));
if i > 0 && arg.ends_with(".pdf") && std::path::Path::new(arg).exists() {
add_log(format!("📂 File argument detected: {}", arg));
set_opened_file(arg.clone());
break; // Only handle the first PDF file
}
}
}
/// Handle runtime file open events (for future single-instance support)
2025-07-15 19:37:57 +01:00
#[allow(dead_code)]
2025-07-15 18:29:42 +01:00
pub fn handle_runtime_file_open(file_path: String) {
if file_path.ends_with(".pdf") && std::path::Path::new(&file_path).exists() {
add_log(format!("📂 Runtime file open: {}", file_path));
set_opened_file(file_path);
}
}
#[cfg(target_os = "macos")]
mod macos_native {
use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::{Class, Object, Sel};
use cocoa::appkit::NSApplication;
use cocoa::base::{id, nil};
use once_cell::sync::Lazy;
use std::sync::Mutex;
2025-07-15 19:37:57 +01:00
use tauri::{AppHandle, Emitter};
2025-07-15 18:29:42 +01:00
use crate::utils::add_log;
use crate::commands::set_opened_file;
2025-07-15 18:49:03 +01:00
// Static app handle storage
static APP_HANDLE: Lazy<Mutex<Option<AppHandle<tauri::Wry>>>> = Lazy::new(|| Mutex::new(None));
2025-07-15 18:29:42 +01:00
2025-07-16 15:49:53 +01:00
// Store files opened during launch
static LAUNCH_FILES: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
// Track if app has finished launching
static APP_FINISHED_LAUNCHING: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
extern "C" fn application_did_finish_launching(_self: &Object, _cmd: Sel, _notification: id) {
add_log("🚀 applicationDidFinishLaunching called".to_string());
*APP_FINISHED_LAUNCHING.lock().unwrap() = true;
// Process any files that were opened during launch
let launch_files = {
let mut files = LAUNCH_FILES.lock().unwrap();
let result = files.clone();
files.clear();
result
};
for file_path in launch_files {
add_log(format!("📂 Processing launch file: {}", file_path));
set_opened_file(file_path.clone());
if let Some(app) = APP_HANDLE.lock().unwrap().as_ref() {
let _ = app.emit("macos://open-file", file_path.clone());
add_log(format!("✅ Emitted launch file event: {}", file_path));
}
}
}
2025-07-15 19:37:57 +01:00
2025-07-15 18:29:42 +01:00
extern "C" fn open_file(_self: &Object, _cmd: Sel, _sender: id, filename: id) -> bool {
unsafe {
let cstr = {
let bytes: *const std::os::raw::c_char = msg_send![filename, UTF8String];
std::ffi::CStr::from_ptr(bytes)
};
if let Ok(path) = cstr.to_str() {
add_log(format!("📂 macOS native file open event: {}", path));
if path.ends_with(".pdf") {
2025-07-16 15:49:53 +01:00
let app_finished = *APP_FINISHED_LAUNCHING.lock().unwrap();
2025-07-15 18:29:42 +01:00
2025-07-16 15:49:53 +01:00
if app_finished {
// App is running, handle immediately
add_log(format!("✅ App running, handling file immediately: {}", path));
set_opened_file(path.to_string());
if let Some(app) = APP_HANDLE.lock().unwrap().as_ref() {
let _ = app.emit("macos://open-file", path.to_string());
add_log(format!("✅ Emitted file open event: {}", path));
}
2025-07-15 19:37:57 +01:00
} else {
2025-07-16 15:49:53 +01:00
// App is launching, store for later
add_log(format!("🚀 App launching, storing file for later: {}", path));
LAUNCH_FILES.lock().unwrap().push(path.to_string());
2025-07-15 18:29:42 +01:00
}
}
}
}
true
}
2025-07-16 10:52:38 +01:00
// Register the delegate immediately when the module loads
pub fn register_delegate_early() {
add_log("🔧 Registering macOS delegate early...".to_string());
2025-07-15 18:29:42 +01:00
unsafe {
2025-07-16 15:49:53 +01:00
let delegate_class = Class::get("StirlingAppDelegate").unwrap_or_else(|| {
2025-07-15 18:29:42 +01:00
let superclass = class!(NSObject);
2025-07-16 15:49:53 +01:00
let mut decl = objc::declare::ClassDecl::new("StirlingAppDelegate", superclass).unwrap();
// Add both delegate methods
decl.add_method(
sel!(applicationDidFinishLaunching:),
application_did_finish_launching as extern "C" fn(&Object, Sel, id)
);
decl.add_method(
sel!(application:openFile:),
open_file as extern "C" fn(&Object, Sel, id, id) -> bool
);
2025-07-15 18:29:42 +01:00
decl.register()
});
let delegate: id = msg_send![delegate_class, new];
let ns_app = NSApplication::sharedApplication(nil);
2025-07-15 18:49:03 +01:00
let _: () = msg_send![ns_app, setDelegate:delegate];
2025-07-15 18:29:42 +01:00
}
2025-07-16 10:52:38 +01:00
add_log("✅ macOS delegate registered early".to_string());
}
pub fn register_open_file_handler(app: &AppHandle<tauri::Wry>) {
add_log("🔧 Connecting app handle to file handler...".to_string());
// Store the app handle
*APP_HANDLE.lock().unwrap() = Some(app.clone());
2025-07-16 15:49:53 +01:00
// If app has finished launching and there are launch files, process them now
if *APP_FINISHED_LAUNCHING.lock().unwrap() {
let launch_files = {
let mut files = LAUNCH_FILES.lock().unwrap();
let result = files.clone();
files.clear();
result
};
for file_path in launch_files {
add_log(format!("📂 Processing stored launch file: {}", file_path));
set_opened_file(file_path.clone());
let _ = app.emit("macos://open-file", file_path);
}
2025-07-15 19:37:57 +01:00
}
2025-07-16 10:52:38 +01:00
add_log("✅ macOS file handler connected successfully".to_string());
2025-07-15 18:29:42 +01:00
}
}