1use serde::{Deserialize, Serialize};
4
5use crate::{MaaError, MaaResult, common, sys};
6use std::ffi::{CStr, CString};
7use std::path::{Path, PathBuf};
8use std::sync::Once;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct AdbDevice {
13 pub name: String,
15 pub adb_path: PathBuf,
17 pub address: String,
19 pub screencap_methods: u64,
21 pub input_methods: u64,
23 pub config: serde_json::Value,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct DesktopWindow {
30 pub hwnd: usize,
32 pub class_name: String,
34 pub window_name: String,
36}
37
38pub struct Toolkit;
40
41static AGENT_SERVER_INIT_OPTION_WARNING: Once = Once::new();
42
43impl Toolkit {
44 #[inline]
45 fn unsupported(api: &str) -> MaaError {
46 MaaError::UnsupportedInAgentServer(api.to_string())
47 }
48
49 fn maybe_warn_init_option_in_agent_server() {
50 if std::env::var_os("MAA_RUST_WARN_AGENTSERVER_TOOLKIT_INIT").is_none() {
51 return;
52 }
53
54 AGENT_SERVER_INIT_OPTION_WARNING.call_once(|| {
55 eprintln!(
56 "Warning: Toolkit::init_option is deprecated in AgentServer; only log_dir is applied."
57 );
58 });
59 }
60
61 pub fn init_option(user_path: &str, default_config: &str) -> MaaResult<()> {
67 if crate::is_agent_server_context() {
68 let _ = default_config;
69 Self::maybe_warn_init_option_in_agent_server();
70 let log_dir = Path::new(user_path).join("debug");
71 return crate::configure_logging(log_dir.to_string_lossy().as_ref());
72 }
73
74 let c_path = CString::new(user_path)?;
75 let c_config = CString::new(default_config)?;
76 let ret = unsafe { sys::MaaToolkitConfigInitOption(c_path.as_ptr(), c_config.as_ptr()) };
77 common::check_bool(ret)
78 }
79
80 pub fn find_adb_devices() -> MaaResult<Vec<AdbDevice>> {
87 if crate::is_agent_server_context() {
88 return Err(Self::unsupported("Toolkit::find_adb_devices"));
89 }
90 Self::find_adb_devices_impl(None)
91 }
92
93 pub fn find_adb_devices_with_adb(adb_path: &str) -> MaaResult<Vec<AdbDevice>> {
101 if crate::is_agent_server_context() {
102 return Err(Self::unsupported("Toolkit::find_adb_devices_with_adb"));
103 }
104 Self::find_adb_devices_impl(Some(adb_path))
105 }
106
107 fn find_adb_devices_impl(specified_adb: Option<&str>) -> MaaResult<Vec<AdbDevice>> {
108 let list = unsafe { sys::MaaToolkitAdbDeviceListCreate() };
109 if list.is_null() {
110 return Err(MaaError::NullPointer);
111 }
112
113 let _guard = AdbDeviceListGuard(list);
114
115 unsafe {
116 let ret = if let Some(adb_path) = specified_adb {
117 let c_path = CString::new(adb_path)?;
118 sys::MaaToolkitAdbDeviceFindSpecified(c_path.as_ptr(), list)
119 } else {
120 sys::MaaToolkitAdbDeviceFind(list)
121 };
122 common::check_bool(ret)?;
123
124 let count = sys::MaaToolkitAdbDeviceListSize(list);
125 let mut devices = Vec::with_capacity(count as usize);
126
127 for i in 0..count {
128 let device_ptr = sys::MaaToolkitAdbDeviceListAt(list, i);
129 if device_ptr.is_null() {
130 continue;
131 }
132
133 let name = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetName(device_ptr))
134 .to_string_lossy()
135 .into_owned();
136
137 let adb_path_str = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAdbPath(device_ptr))
138 .to_string_lossy()
139 .into_owned();
140
141 let address = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAddress(device_ptr))
142 .to_string_lossy()
143 .into_owned();
144
145 let screencap_methods =
146 sys::MaaToolkitAdbDeviceGetScreencapMethods(device_ptr) as u64;
147 let input_methods = sys::MaaToolkitAdbDeviceGetInputMethods(device_ptr) as u64;
148
149 let config_str =
150 CStr::from_ptr(sys::MaaToolkitAdbDeviceGetConfig(device_ptr)).to_string_lossy();
151 let config = serde_json::from_str(&config_str).unwrap_or(serde_json::Value::Null);
152
153 devices.push(AdbDevice {
154 name,
155 adb_path: PathBuf::from(adb_path_str),
156 address,
157 screencap_methods,
158 input_methods,
159 config,
160 });
161 }
162 Ok(devices)
163 }
164 }
165
166 pub fn find_desktop_windows() -> MaaResult<Vec<DesktopWindow>> {
171 if crate::is_agent_server_context() {
172 return Err(Self::unsupported("Toolkit::find_desktop_windows"));
173 }
174
175 let list = unsafe { sys::MaaToolkitDesktopWindowListCreate() };
176 if list.is_null() {
177 return Err(MaaError::NullPointer);
178 }
179
180 let _guard = DesktopWindowListGuard(list);
181
182 unsafe {
183 let ret = sys::MaaToolkitDesktopWindowFindAll(list);
184 common::check_bool(ret)?;
185
186 let count = sys::MaaToolkitDesktopWindowListSize(list);
187 let mut windows = Vec::with_capacity(count as usize);
188
189 for i in 0..count {
190 let win_ptr = sys::MaaToolkitDesktopWindowListAt(list, i);
191 if win_ptr.is_null() {
192 continue;
193 }
194
195 let hwnd = sys::MaaToolkitDesktopWindowGetHandle(win_ptr) as usize;
196
197 let class_name = CStr::from_ptr(sys::MaaToolkitDesktopWindowGetClassName(win_ptr))
198 .to_string_lossy()
199 .into_owned();
200
201 let window_name =
202 CStr::from_ptr(sys::MaaToolkitDesktopWindowGetWindowName(win_ptr))
203 .to_string_lossy()
204 .into_owned();
205
206 windows.push(DesktopWindow {
207 hwnd,
208 class_name,
209 window_name,
210 });
211 }
212 Ok(windows)
213 }
214 }
215}
216
217struct AdbDeviceListGuard(*mut sys::MaaToolkitAdbDeviceList);
218impl Drop for AdbDeviceListGuard {
219 fn drop(&mut self) {
220 unsafe { sys::MaaToolkitAdbDeviceListDestroy(self.0) }
221 }
222}
223
224struct DesktopWindowListGuard(*mut sys::MaaToolkitDesktopWindowList);
225impl Drop for DesktopWindowListGuard {
226 fn drop(&mut self) {
227 unsafe { sys::MaaToolkitDesktopWindowListDestroy(self.0) }
228 }
229}