use alloc::boxed::Box;
use wasefire_applet_api::fingerprint::sensor as api;
use wasefire_common::ptr::SharedPtr;
use wasefire_error::Code;
use wasefire_sync::Mutex;
use crate::{Error, convert, convert_bool, convert_unit};
pub fn is_supported() -> bool {
convert_bool(unsafe { api::is_supported() }).unwrap_or(false)
}
pub struct Image {
pub data: Box<[u8]>,
pub width: usize,
}
pub struct Capture {
state: SharedPtr<Mutex<CaptureState>>,
dropped: bool,
}
impl Drop for Capture {
fn drop(&mut self) {
if !self.dropped {
self.drop_state().unwrap();
}
}
}
enum CaptureState {
Started,
Done { image: Box<[u8]>, width: usize },
Failed { error: Error },
}
impl Capture {
pub fn new() -> Result<Self, Error> {
let handler_func = Self::call;
let state = Box::into_raw(Box::new(Mutex::new(CaptureState::Started)));
let handler_data = state as *const u8;
let params = api::capture::Params { handler_func, handler_data };
convert_unit(unsafe { api::capture(params) })?;
Ok(Capture { state: SharedPtr(state), dropped: false })
}
pub fn is_done(&self) -> bool {
match *Self::state(self.state.0 as *const u8).lock() {
CaptureState::Started => false,
CaptureState::Done { .. } | CaptureState::Failed { .. } => true,
}
}
pub fn result(mut self) -> Result<Image, Error> {
match Self::into_state(&mut self) {
CaptureState::Started => {
let _ = self.abort();
Err(Error::user(Code::InvalidState))
}
CaptureState::Done { image, width } => Ok(Image { data: image, width }),
CaptureState::Failed { error } => Err(error),
}
}
pub fn abort(mut self) -> Result<(), Error> {
self.drop_state()
}
fn drop_state(&mut self) -> Result<(), Error> {
if matches!(Self::into_state(self), CaptureState::Started) {
convert_unit(unsafe { api::abort_capture() })?;
}
Ok(())
}
extern "C" fn call(data: *const u8, ptr: *mut u8, width: isize, height: usize) {
*Self::state(data).lock() = match convert(width) {
Ok(width) => {
assert!(0 < width);
let len = width * height;
let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
let image = unsafe { Box::from_raw(ptr) };
CaptureState::Done { image, width }
}
Err(error) => CaptureState::Failed { error },
}
}
fn state<'a>(data: *const u8) -> &'a Mutex<CaptureState> {
unsafe { &*(data as *const Mutex<CaptureState>) }
}
#[allow(clippy::wrong_self_convention)]
fn into_state(&mut self) -> CaptureState {
assert!(!self.dropped);
self.dropped = true;
unsafe { Box::from_raw(self.state.0 as *mut Mutex<CaptureState>) }.into_inner()
}
}