use serde::{Deserialize, Serialize};
use crate::{MaaError, MaaResult, common, sys};
use std::ffi::{CStr, CString};
use std::path::{Path, PathBuf};
use std::sync::Once;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdbDevice {
pub name: String,
pub adb_path: PathBuf,
pub address: String,
pub screencap_methods: u64,
pub input_methods: u64,
pub config: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DesktopWindow {
pub hwnd: usize,
pub class_name: String,
pub window_name: String,
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MacOSPermission {
ScreenCapture = sys::MaaMacOSPermissionEnum_MaaMacOSPermissionScreenCapture as i32,
Accessibility = sys::MaaMacOSPermissionEnum_MaaMacOSPermissionAccessibility as i32,
}
pub struct Toolkit;
static AGENT_SERVER_INIT_OPTION_WARNING: Once = Once::new();
impl Toolkit {
#[inline]
fn unsupported(api: &str) -> MaaError {
MaaError::UnsupportedInAgentServer(api.to_string())
}
fn maybe_warn_init_option_in_agent_server() {
if std::env::var_os("MAA_RUST_WARN_AGENTSERVER_TOOLKIT_INIT").is_none() {
return;
}
AGENT_SERVER_INIT_OPTION_WARNING.call_once(|| {
eprintln!(
"Warning: Toolkit::init_option is deprecated in AgentServer; only log_dir is applied."
);
});
}
pub fn init_option(user_path: &str, default_config: &str) -> MaaResult<()> {
if crate::is_agent_server_context() {
let _ = default_config;
Self::maybe_warn_init_option_in_agent_server();
let log_dir = Path::new(user_path).join("debug");
return crate::configure_logging(log_dir.to_string_lossy().as_ref());
}
let c_path = CString::new(user_path)?;
let c_config = CString::new(default_config)?;
let ret = unsafe { sys::MaaToolkitConfigInitOption(c_path.as_ptr(), c_config.as_ptr()) };
common::check_bool(ret)
}
pub fn find_adb_devices() -> MaaResult<Vec<AdbDevice>> {
if crate::is_agent_server_context() {
return Err(Self::unsupported("Toolkit::find_adb_devices"));
}
Self::find_adb_devices_impl(None)
}
pub fn find_adb_devices_with_adb(adb_path: &str) -> MaaResult<Vec<AdbDevice>> {
if crate::is_agent_server_context() {
return Err(Self::unsupported("Toolkit::find_adb_devices_with_adb"));
}
Self::find_adb_devices_impl(Some(adb_path))
}
fn find_adb_devices_impl(specified_adb: Option<&str>) -> MaaResult<Vec<AdbDevice>> {
let list = unsafe { sys::MaaToolkitAdbDeviceListCreate() };
if list.is_null() {
return Err(MaaError::NullPointer);
}
let _guard = AdbDeviceListGuard(list);
unsafe {
let ret = if let Some(adb_path) = specified_adb {
let c_path = CString::new(adb_path)?;
sys::MaaToolkitAdbDeviceFindSpecified(c_path.as_ptr(), list)
} else {
sys::MaaToolkitAdbDeviceFind(list)
};
common::check_bool(ret)?;
let count = sys::MaaToolkitAdbDeviceListSize(list);
let mut devices = Vec::with_capacity(count as usize);
for i in 0..count {
let device_ptr = sys::MaaToolkitAdbDeviceListAt(list, i);
if device_ptr.is_null() {
continue;
}
let name = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetName(device_ptr))
.to_string_lossy()
.into_owned();
let adb_path_str = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAdbPath(device_ptr))
.to_string_lossy()
.into_owned();
let address = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAddress(device_ptr))
.to_string_lossy()
.into_owned();
let screencap_methods =
sys::MaaToolkitAdbDeviceGetScreencapMethods(device_ptr) as u64;
let input_methods = sys::MaaToolkitAdbDeviceGetInputMethods(device_ptr) as u64;
let config_str =
CStr::from_ptr(sys::MaaToolkitAdbDeviceGetConfig(device_ptr)).to_string_lossy();
let config = serde_json::from_str(&config_str).unwrap_or(serde_json::Value::Null);
devices.push(AdbDevice {
name,
adb_path: PathBuf::from(adb_path_str),
address,
screencap_methods,
input_methods,
config,
});
}
Ok(devices)
}
}
pub fn find_desktop_windows() -> MaaResult<Vec<DesktopWindow>> {
if crate::is_agent_server_context() {
return Err(Self::unsupported("Toolkit::find_desktop_windows"));
}
let list = unsafe { sys::MaaToolkitDesktopWindowListCreate() };
if list.is_null() {
return Err(MaaError::NullPointer);
}
let _guard = DesktopWindowListGuard(list);
unsafe {
let ret = sys::MaaToolkitDesktopWindowFindAll(list);
common::check_bool(ret)?;
let count = sys::MaaToolkitDesktopWindowListSize(list);
let mut windows = Vec::with_capacity(count as usize);
for i in 0..count {
let win_ptr = sys::MaaToolkitDesktopWindowListAt(list, i);
if win_ptr.is_null() {
continue;
}
let hwnd = sys::MaaToolkitDesktopWindowGetHandle(win_ptr) as usize;
let class_name = CStr::from_ptr(sys::MaaToolkitDesktopWindowGetClassName(win_ptr))
.to_string_lossy()
.into_owned();
let window_name =
CStr::from_ptr(sys::MaaToolkitDesktopWindowGetWindowName(win_ptr))
.to_string_lossy()
.into_owned();
windows.push(DesktopWindow {
hwnd,
class_name,
window_name,
});
}
Ok(windows)
}
}
pub fn macos_check_permission(permission: MacOSPermission) -> MaaResult<bool> {
if crate::is_agent_server_context() {
return Err(Self::unsupported("Toolkit::macos_check_permission"));
}
let ret =
unsafe { sys::MaaToolkitMacOSCheckPermission(permission as sys::MaaMacOSPermission) };
Ok(ret != 0)
}
pub fn macos_request_permission(permission: MacOSPermission) -> MaaResult<bool> {
if crate::is_agent_server_context() {
return Err(Self::unsupported("Toolkit::macos_request_permission"));
}
let ret =
unsafe { sys::MaaToolkitMacOSRequestPermission(permission as sys::MaaMacOSPermission) };
Ok(ret != 0)
}
pub fn macos_reveal_permission_settings(permission: MacOSPermission) -> MaaResult<bool> {
if crate::is_agent_server_context() {
return Err(Self::unsupported(
"Toolkit::macos_reveal_permission_settings",
));
}
let ret = unsafe {
sys::MaaToolkitMacOSRevealPermissionSettings(permission as sys::MaaMacOSPermission)
};
Ok(ret != 0)
}
}
struct AdbDeviceListGuard(*mut sys::MaaToolkitAdbDeviceList);
impl Drop for AdbDeviceListGuard {
fn drop(&mut self) {
unsafe { sys::MaaToolkitAdbDeviceListDestroy(self.0) }
}
}
struct DesktopWindowListGuard(*mut sys::MaaToolkitDesktopWindowList);
impl Drop for DesktopWindowListGuard {
fn drop(&mut self) {
unsafe { sys::MaaToolkitDesktopWindowListDestroy(self.0) }
}
}