use crate::context::Context;
use crate::resource::Resource;
use crate::{MaaError, MaaResult, common, sys};
use std::ffi::{CStr, CString};
use std::os::raw::c_void;
pub trait CustomAction: Send + Sync {
fn run(
&self,
context: &Context,
task_id: common::MaaId,
node_name: &str,
custom_action_name: &str,
custom_action_param: &str,
reco_id: common::MaaId,
box_rect: &common::Rect,
) -> bool;
}
pub trait CustomRecognition: Send + Sync {
fn analyze(
&self,
context: &Context,
task_id: common::MaaId,
node_name: &str,
custom_recognition_name: &str,
custom_recognition_param: &str,
image: &crate::buffer::MaaImageBuffer,
roi: &common::Rect,
) -> Option<(common::Rect, String)>;
}
pub struct FnRecognition<F>
where
F: Fn(&Context, &RecognitionArgs) -> Option<(common::Rect, String)> + Send + Sync,
{
func: F,
}
pub struct RecognitionArgs<'a> {
pub task_id: common::MaaId,
pub node_name: &'a str,
pub name: &'a str,
pub param: &'a str,
pub image: &'a crate::buffer::MaaImageBuffer,
pub roi: &'a common::Rect,
}
impl<F> FnRecognition<F>
where
F: Fn(&Context, &RecognitionArgs) -> Option<(common::Rect, String)> + Send + Sync,
{
pub fn new(func: F) -> Self {
Self { func }
}
}
impl<F> CustomRecognition for FnRecognition<F>
where
F: Fn(&Context, &RecognitionArgs) -> Option<(common::Rect, String)> + Send + Sync,
{
fn analyze(
&self,
context: &Context,
task_id: common::MaaId,
node_name: &str,
custom_recognition_name: &str,
custom_recognition_param: &str,
image: &crate::buffer::MaaImageBuffer,
roi: &common::Rect,
) -> Option<(common::Rect, String)> {
let args = RecognitionArgs {
task_id,
node_name,
name: custom_recognition_name,
param: custom_recognition_param,
image,
roi,
};
(self.func)(context, &args)
}
}
pub struct FnAction<F>
where
F: Fn(&Context, &ActionArgs) -> bool + Send + Sync,
{
func: F,
}
pub struct ActionArgs<'a> {
pub task_id: common::MaaId,
pub node_name: &'a str,
pub name: &'a str,
pub param: &'a str,
pub reco_id: common::MaaId,
pub box_rect: &'a common::Rect,
}
impl<F> FnAction<F>
where
F: Fn(&Context, &ActionArgs) -> bool + Send + Sync,
{
pub fn new(func: F) -> Self {
Self { func }
}
}
impl<F> CustomAction for FnAction<F>
where
F: Fn(&Context, &ActionArgs) -> bool + Send + Sync,
{
fn run(
&self,
context: &Context,
task_id: common::MaaId,
node_name: &str,
custom_action_name: &str,
custom_action_param: &str,
reco_id: common::MaaId,
box_rect: &common::Rect,
) -> bool {
let args = ActionArgs {
task_id,
node_name,
name: custom_action_name,
param: custom_action_param,
reco_id,
box_rect,
};
(self.func)(context, &args)
}
}
pub(crate) unsafe extern "C" fn custom_action_trampoline(
context: *mut sys::MaaContext,
task_id: sys::MaaTaskId,
node_name: *const std::os::raw::c_char,
custom_action_name: *const std::os::raw::c_char,
custom_action_param: *const std::os::raw::c_char,
reco_id: sys::MaaRecoId,
box_: *const sys::MaaRect,
trans_arg: *mut std::os::raw::c_void,
) -> sys::MaaBool {
if trans_arg.is_null() {
return 0; }
let action = unsafe { &*(trans_arg as *mut Box<dyn CustomAction>) };
let ctx = match unsafe { Context::from_raw(context) } {
Some(c) => c,
None => return 0,
};
let get_str = |ptr: *const std::os::raw::c_char| -> &str {
if ptr.is_null() {
""
} else {
unsafe { CStr::from_ptr(ptr).to_str().unwrap_or("") }
}
};
let node = get_str(node_name);
let name = get_str(custom_action_name);
let param = get_str(custom_action_param);
let rect = if !box_.is_null() {
crate::buffer::MaaRectBuffer::from_handle(box_ as *mut sys::MaaRect)
.map(|buf| buf.get())
.unwrap_or(common::Rect::default())
} else {
common::Rect::default()
};
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
action.run(&ctx, task_id, node, name, param, reco_id, &rect)
}));
match result {
Ok(true) => 1,
Ok(false) => 0,
Err(_) => {
eprintln!("MaaFramework Rust Binding: Panic caught in custom action callback");
0
}
}
}
pub(crate) unsafe extern "C" fn custom_recognition_trampoline(
context: *mut sys::MaaContext,
task_id: sys::MaaTaskId,
node_name: *const std::os::raw::c_char,
custom_recognition_name: *const std::os::raw::c_char,
custom_recognition_param: *const std::os::raw::c_char,
image: *const sys::MaaImageBuffer,
roi: *const sys::MaaRect,
trans_arg: *mut std::os::raw::c_void,
out_box: *mut sys::MaaRect,
out_detail: *mut sys::MaaStringBuffer,
) -> sys::MaaBool {
if trans_arg.is_null() {
return 0;
}
let reco = unsafe { &*(trans_arg as *mut Box<dyn CustomRecognition>) };
let ctx = match unsafe { Context::from_raw(context) } {
Some(c) => c,
None => return 0,
};
let get_str = |ptr: *const std::os::raw::c_char| -> &str {
if ptr.is_null() {
""
} else {
unsafe { CStr::from_ptr(ptr).to_str().unwrap_or("") }
}
};
let node = get_str(node_name);
let name = get_str(custom_recognition_name);
let param = get_str(custom_recognition_param);
let img_buf = crate::buffer::MaaImageBuffer::from_handle(image as *mut sys::MaaImageBuffer);
if img_buf.is_none() {
return 0;
}
let img_buf = img_buf.unwrap();
let roi_rect = if !roi.is_null() {
crate::buffer::MaaRectBuffer::from_handle(roi as *mut sys::MaaRect)
.map(|buf| buf.get())
.unwrap_or(common::Rect::default())
} else {
common::Rect::default()
};
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
reco.analyze(&ctx, task_id, node, name, param, &img_buf, &roi_rect)
}));
match result {
Ok(Some((res_rect, res_detail))) => {
if !out_box.is_null() {
if let Some(mut out_rect_buf) = crate::buffer::MaaRectBuffer::from_handle(out_box) {
let _ = out_rect_buf.set(&res_rect);
}
}
if !out_detail.is_null() {
if let Some(mut out_str_buf) =
crate::buffer::MaaStringBuffer::from_handle(out_detail)
{
let _ = out_str_buf.set(&res_detail);
}
}
1
}
Ok(None) => 0,
Err(_) => {
eprintln!("MaaFramework Rust Binding: Panic caught in custom recognition callback");
0
}
}
}
impl Resource {
pub fn register_custom_action(
&self,
name: &str,
action: Box<dyn CustomAction>,
) -> MaaResult<()> {
let c_name = CString::new(name)?;
let action_ptr = Box::into_raw(Box::new(action));
let action_ptr_void = action_ptr as *mut c_void;
unsafe {
let ret = sys::MaaResourceRegisterCustomAction(
self.raw(),
c_name.as_ptr(),
Some(custom_action_trampoline),
action_ptr_void,
);
if ret == 0 {
let _ = Box::from_raw(action_ptr);
return Err(MaaError::FrameworkError(0));
}
}
let mut map = self.custom_actions().lock().unwrap();
if let Some(old_ptr) = map.insert(name.to_string(), action_ptr as usize) {
unsafe {
let _ = Box::from_raw(old_ptr as *mut Box<dyn CustomAction>);
}
}
Ok(())
}
pub fn register_custom_recognition(
&self,
name: &str,
reco: Box<dyn CustomRecognition>,
) -> MaaResult<()> {
let c_name = CString::new(name)?;
let reco_ptr = Box::into_raw(Box::new(reco));
let reco_ptr_void = reco_ptr as *mut c_void;
unsafe {
let ret = sys::MaaResourceRegisterCustomRecognition(
self.raw(),
c_name.as_ptr(),
Some(custom_recognition_trampoline),
reco_ptr_void,
);
if ret == 0 {
let _ = Box::from_raw(reco_ptr);
return Err(MaaError::FrameworkError(0));
}
}
let mut map = self.custom_recognitions().lock().unwrap();
if let Some(old_ptr) = map.insert(name.to_string(), reco_ptr as usize) {
unsafe {
let _ = Box::from_raw(old_ptr as *mut Box<dyn CustomRecognition>);
}
}
Ok(())
}
}