use crate::bindings as ffi;
use crate::printer::Printer;
use std::collections::HashMap;
use std::panic::AssertUnwindSafe;
use std::sync::{Arc, Mutex, OnceLock};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PrinterUpdate {
Added,
Removed,
StateChanged,
}
impl PrinterUpdate {
pub(crate) fn from_raw(update: ffi::cpdb_printer_update_t) -> Option<Self> {
match update as i64 {
0 => Some(Self::Added),
1 => Some(Self::Removed),
2 => Some(Self::StateChanged),
_ => None,
}
}
}
type PrinterObserver = dyn FnMut(&Printer<'_>, PrinterUpdate) + Send;
type ObserverSlot = Arc<Mutex<Box<PrinterObserver>>>;
fn registry() -> &'static Mutex<HashMap<usize, ObserverSlot>> {
static R: OnceLock<Mutex<HashMap<usize, ObserverSlot>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(HashMap::new()))
}
fn lock_registry() -> std::sync::MutexGuard<'static, HashMap<usize, ObserverSlot>> {
registry().lock().unwrap_or_else(|p| p.into_inner())
}
pub(crate) fn register_printer_observer(
frontend: *mut ffi::cpdb_frontend_obj_t,
observer: Box<PrinterObserver>,
) {
lock_registry().insert(frontend as usize, Arc::new(Mutex::new(observer)));
}
pub(crate) fn unregister_printer_observer(frontend: *mut ffi::cpdb_frontend_obj_t) {
lock_registry().remove(&(frontend as usize));
}
pub(crate) unsafe extern "C" fn printer_trampoline(
frontend: *mut ffi::cpdb_frontend_obj_t,
printer: *mut ffi::cpdb_printer_obj_t,
update: ffi::cpdb_printer_update_t,
) {
let slot = {
let map = lock_registry();
match map.get(&(frontend as usize)) {
Some(slot) => Arc::clone(slot),
None => return,
}
};
let Some(update) = PrinterUpdate::from_raw(update) else {
return;
};
let Ok(printer) = Printer::from_raw_borrowed(printer) else {
return;
};
let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
let mut closure = slot.lock().unwrap_or_else(|p| p.into_inner());
closure(&printer, update);
}));
}
pub(crate) type AcquireCompletion = dyn FnOnce(&Printer<'_>, bool) + Send;
pub(crate) fn into_completion_user_data(completion: Box<AcquireCompletion>) -> *mut libc::c_void {
Box::into_raw(Box::new(completion)) as *mut libc::c_void
}
pub(crate) unsafe extern "C" fn acquire_trampoline(
printer: *mut ffi::cpdb_printer_obj_t,
status: libc::c_int,
user_data: *mut libc::c_void,
) {
if user_data.is_null() {
return;
}
let outer: Box<Box<AcquireCompletion>> =
unsafe { Box::from_raw(user_data as *mut Box<AcquireCompletion>) };
let Ok(printer) = Printer::from_raw_borrowed(printer) else {
return;
};
let closure: Box<AcquireCompletion> = *outer;
let _ = std::panic::catch_unwind(AssertUnwindSafe(move || {
closure(&printer, status != 0);
}));
}