dbx/src-tauri/src/macos_app_delegate.rs

70 lines
2.4 KiB
Rust

use std::{
ffi::CStr,
sync::{Once, OnceLock},
};
use objc2::{
ffi,
runtime::{AnyClass, AnyObject, Imp, Sel},
sel,
};
use objc2_app_kit::NSApplicationTerminateReply;
use tauri::{AppHandle, Manager};
use crate::{request_app_close, CloseBehaviorState};
static APP_HANDLE: OnceLock<AppHandle> = OnceLock::new();
static INSTALL_DOCK_QUIT_HANDLER: Once = Once::new();
pub(crate) fn install_dock_quit_handler(app: &AppHandle) {
let _ = APP_HANDLE.set(app.clone());
INSTALL_DOCK_QUIT_HANDLER.call_once(|| {
let Some(delegate_class) =
AnyClass::get(CStr::from_bytes_with_nul(b"TaoAppDelegateParent\0").expect("valid class name"))
else {
eprintln!("[WARN] failed to install macOS Dock quit handler: TaoAppDelegateParent not found");
return;
};
let imp: Imp = unsafe {
std::mem::transmute(
application_should_terminate
as extern "C-unwind" fn(&AnyObject, Sel, &AnyObject) -> NSApplicationTerminateReply,
)
};
let method_types = CStr::from_bytes_with_nul(b"Q@:@\0").expect("valid Objective-C method type encoding");
// Tao's app delegate does not implement applicationShouldTerminate:, so Dock Quit can
// bypass Tauri's ExitRequested event. Add the method to Tao's registered delegate class
// and route that native quit request through DBX's normal close confirmation flow.
let added = unsafe {
ffi::class_addMethod(
delegate_class as *const AnyClass as *mut AnyClass,
sel!(applicationShouldTerminate:),
imp,
method_types.as_ptr(),
)
};
if !added.as_bool() {
eprintln!("[WARN] failed to install macOS Dock quit handler: applicationShouldTerminate: already exists");
}
});
}
extern "C-unwind" fn application_should_terminate(
_delegate: &AnyObject,
_selector: Sel,
_sender: &AnyObject,
) -> NSApplicationTerminateReply {
let Some(app) = APP_HANDLE.get() else {
return NSApplicationTerminateReply::TerminateNow;
};
if app.try_state::<CloseBehaviorState>().map(|state| state.take_confirmed_exit()).unwrap_or(false) {
return NSApplicationTerminateReply::TerminateNow;
}
request_app_close(app, "quit");
NSApplicationTerminateReply::TerminateCancel
}