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
38#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct GamescopeInstance {
44 pub display_no: u32,
46 pub pipewire_node_id: u32,
49 pub eis_socket_path: String,
52}
53
54#[repr(i32)]
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
57pub enum MacOSPermission {
58 ScreenCapture = sys::MaaMacOSPermissionEnum_MaaMacOSPermissionScreenCapture as i32,
60 Accessibility = sys::MaaMacOSPermissionEnum_MaaMacOSPermissionAccessibility as i32,
62}
63
64pub struct Toolkit;
66
67static AGENT_SERVER_INIT_OPTION_WARNING: Once = Once::new();
68
69impl Toolkit {
70 #[inline]
71 fn unsupported(api: &str) -> MaaError {
72 MaaError::UnsupportedInAgentServer(api.to_string())
73 }
74
75 fn maybe_warn_init_option_in_agent_server() {
76 if std::env::var_os("MAA_RUST_WARN_AGENTSERVER_TOOLKIT_INIT").is_none() {
77 return;
78 }
79
80 AGENT_SERVER_INIT_OPTION_WARNING.call_once(|| {
81 eprintln!(
82 "Warning: Toolkit::init_option is deprecated in AgentServer; only log_dir is applied."
83 );
84 });
85 }
86
87 pub fn init_option(user_path: &str, default_config: &str) -> MaaResult<()> {
93 if crate::is_agent_server_context() {
94 let _ = default_config;
95 Self::maybe_warn_init_option_in_agent_server();
96 let log_dir = Path::new(user_path).join("debug");
97 return crate::configure_logging(log_dir.to_string_lossy().as_ref());
98 }
99
100 let c_path = CString::new(user_path)?;
101 let c_config = CString::new(default_config)?;
102 let ret = unsafe { sys::MaaToolkitConfigInitOption(c_path.as_ptr(), c_config.as_ptr()) };
103 common::check_bool(ret)
104 }
105
106 pub fn find_adb_devices() -> MaaResult<Vec<AdbDevice>> {
113 if crate::is_agent_server_context() {
114 return Err(Self::unsupported("Toolkit::find_adb_devices"));
115 }
116 Self::find_adb_devices_impl(None)
117 }
118
119 pub fn find_adb_devices_with_adb(adb_path: &str) -> MaaResult<Vec<AdbDevice>> {
127 if crate::is_agent_server_context() {
128 return Err(Self::unsupported("Toolkit::find_adb_devices_with_adb"));
129 }
130 Self::find_adb_devices_impl(Some(adb_path))
131 }
132
133 fn find_adb_devices_impl(specified_adb: Option<&str>) -> MaaResult<Vec<AdbDevice>> {
134 let list = unsafe { sys::MaaToolkitAdbDeviceListCreate() };
135 if list.is_null() {
136 return Err(MaaError::NullPointer);
137 }
138
139 let _guard = AdbDeviceListGuard(list);
140
141 unsafe {
142 let ret = if let Some(adb_path) = specified_adb {
143 let c_path = CString::new(adb_path)?;
144 sys::MaaToolkitAdbDeviceFindSpecified(c_path.as_ptr(), list)
145 } else {
146 sys::MaaToolkitAdbDeviceFind(list)
147 };
148 common::check_bool(ret)?;
149
150 let count = sys::MaaToolkitAdbDeviceListSize(list);
151 let mut devices = Vec::with_capacity(count as usize);
152
153 for i in 0..count {
154 let device_ptr = sys::MaaToolkitAdbDeviceListAt(list, i);
155 if device_ptr.is_null() {
156 continue;
157 }
158
159 let name = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetName(device_ptr))
160 .to_string_lossy()
161 .into_owned();
162
163 let adb_path_str = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAdbPath(device_ptr))
164 .to_string_lossy()
165 .into_owned();
166
167 let address = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAddress(device_ptr))
168 .to_string_lossy()
169 .into_owned();
170
171 let screencap_methods =
172 sys::MaaToolkitAdbDeviceGetScreencapMethods(device_ptr) as u64;
173 let input_methods = sys::MaaToolkitAdbDeviceGetInputMethods(device_ptr) as u64;
174
175 let config_str =
176 CStr::from_ptr(sys::MaaToolkitAdbDeviceGetConfig(device_ptr)).to_string_lossy();
177 let config = serde_json::from_str(&config_str).unwrap_or(serde_json::Value::Null);
178
179 devices.push(AdbDevice {
180 name,
181 adb_path: PathBuf::from(adb_path_str),
182 address,
183 screencap_methods,
184 input_methods,
185 config,
186 });
187 }
188 Ok(devices)
189 }
190 }
191
192 pub fn find_desktop_windows() -> MaaResult<Vec<DesktopWindow>> {
197 if crate::is_agent_server_context() {
198 return Err(Self::unsupported("Toolkit::find_desktop_windows"));
199 }
200
201 let list = unsafe { sys::MaaToolkitDesktopWindowListCreate() };
202 if list.is_null() {
203 return Err(MaaError::NullPointer);
204 }
205
206 let _guard = DesktopWindowListGuard(list);
207
208 unsafe {
209 let ret = sys::MaaToolkitDesktopWindowFindAll(list);
210 common::check_bool(ret)?;
211
212 let count = sys::MaaToolkitDesktopWindowListSize(list);
213 let mut windows = Vec::with_capacity(count as usize);
214
215 for i in 0..count {
216 let win_ptr = sys::MaaToolkitDesktopWindowListAt(list, i);
217 if win_ptr.is_null() {
218 continue;
219 }
220
221 let hwnd = sys::MaaToolkitDesktopWindowGetHandle(win_ptr) as usize;
222
223 let class_name = CStr::from_ptr(sys::MaaToolkitDesktopWindowGetClassName(win_ptr))
224 .to_string_lossy()
225 .into_owned();
226
227 let window_name =
228 CStr::from_ptr(sys::MaaToolkitDesktopWindowGetWindowName(win_ptr))
229 .to_string_lossy()
230 .into_owned();
231
232 windows.push(DesktopWindow {
233 hwnd,
234 class_name,
235 window_name,
236 });
237 }
238 Ok(windows)
239 }
240 }
241
242 pub fn macos_check_permission(permission: MacOSPermission) -> MaaResult<bool> {
244 if crate::is_agent_server_context() {
245 return Err(Self::unsupported("Toolkit::macos_check_permission"));
246 }
247
248 let ret =
249 unsafe { sys::MaaToolkitMacOSCheckPermission(permission as sys::MaaMacOSPermission) };
250 Ok(ret != 0)
251 }
252
253 pub fn macos_request_permission(permission: MacOSPermission) -> MaaResult<bool> {
258 if crate::is_agent_server_context() {
259 return Err(Self::unsupported("Toolkit::macos_request_permission"));
260 }
261
262 let ret =
263 unsafe { sys::MaaToolkitMacOSRequestPermission(permission as sys::MaaMacOSPermission) };
264 Ok(ret != 0)
265 }
266
267 pub fn macos_reveal_permission_settings(permission: MacOSPermission) -> MaaResult<bool> {
269 if crate::is_agent_server_context() {
270 return Err(Self::unsupported(
271 "Toolkit::macos_reveal_permission_settings",
272 ));
273 }
274
275 let ret = unsafe {
276 sys::MaaToolkitMacOSRevealPermissionSettings(permission as sys::MaaMacOSPermission)
277 };
278 Ok(ret != 0)
279 }
280
281 pub fn find_gamescope_instances() -> MaaResult<Vec<GamescopeInstance>> {
291 if crate::is_agent_server_context() {
292 return Err(Self::unsupported("Toolkit::find_gamescope_instances"));
293 }
294
295 let list = unsafe { sys::MaaToolkitGamescopeInstanceListCreate() };
296 if list.is_null() {
297 return Err(MaaError::NullPointer);
298 }
299
300 let _guard = GamescopeInstanceListGuard(list);
301
302 unsafe {
303 common::check_bool(sys::MaaToolkitGamescopeInstanceFindAll(list))?;
304
305 let count = sys::MaaToolkitGamescopeInstanceListSize(list);
306 let mut instances = Vec::with_capacity(count as usize);
307
308 for i in 0..count {
309 let instance_ptr = sys::MaaToolkitGamescopeInstanceListAt(list, i);
310 if instance_ptr.is_null() {
311 continue;
312 }
313
314 let display_no = sys::MaaToolkitGamescopeInstanceGetDisplayNo(instance_ptr);
315 let pipewire_node_id =
316 sys::MaaToolkitGamescopeInstanceGetPipeWireNodeId(instance_ptr);
317 let eis_socket_path =
318 sys::MaaToolkitGamescopeInstanceGetEisSocketPath(instance_ptr);
319 if eis_socket_path.is_null() {
320 return Err(MaaError::NullPointer);
321 }
322 let eis_socket_path = CStr::from_ptr(eis_socket_path)
323 .to_string_lossy()
324 .into_owned();
325
326 instances.push(GamescopeInstance {
327 display_no,
328 pipewire_node_id,
329 eis_socket_path,
330 });
331 }
332 Ok(instances)
333 }
334 }
335}
336
337struct AdbDeviceListGuard(*mut sys::MaaToolkitAdbDeviceList);
338impl Drop for AdbDeviceListGuard {
339 fn drop(&mut self) {
340 unsafe { sys::MaaToolkitAdbDeviceListDestroy(self.0) }
341 }
342}
343
344struct DesktopWindowListGuard(*mut sys::MaaToolkitDesktopWindowList);
345impl Drop for DesktopWindowListGuard {
346 fn drop(&mut self) {
347 unsafe { sys::MaaToolkitDesktopWindowListDestroy(self.0) }
348 }
349}
350
351struct GamescopeInstanceListGuard(*mut sys::MaaToolkitGamescopeInstanceList);
352impl Drop for GamescopeInstanceListGuard {
353 fn drop(&mut self) {
354 unsafe { sys::MaaToolkitGamescopeInstanceListDestroy(self.0) }
355 }
356}
357
358pub struct PortalHelper {
367 handle: *mut sys::MaaToolkitPortalHelper,
368}
369
370impl PortalHelper {
371 pub fn new() -> MaaResult<Self> {
373 let handle = unsafe { sys::MaaToolkitPortalHelperCreate() };
374 if handle.is_null() {
375 return Err(MaaError::NullPointer);
376 }
377 Ok(Self { handle })
378 }
379
380 pub fn open_stream(&self) -> MaaResult<()> {
383 common::check_bool(unsafe { sys::MaaToolkitPortalHelperOpenStream(self.handle) })
384 }
385
386 pub fn get_persist(&self) -> bool {
388 unsafe { sys::MaaToolkitPortalHelperGetPersist(self.handle) != 0 }
389 }
390
391 pub fn set_persist(&self, enable: bool) {
393 unsafe { sys::MaaToolkitPortalHelperSetPersist(self.handle, enable as sys::MaaBool) };
394 }
395
396 pub fn get_pipewire_fd(&self) -> i32 {
398 unsafe { sys::MaaToolkitPortalHelperGetPipeWireFD(self.handle) }
399 }
400
401 pub fn get_pipewire_node_id(&self) -> u32 {
403 unsafe { sys::MaaToolkitPortalHelperGetPipeWireNodeID(self.handle) }
404 }
405
406 pub fn get_restore_token(&self) -> String {
410 let ptr = unsafe { sys::MaaToolkitPortalHelperGetRestoreToken(self.handle) };
411 if ptr.is_null() {
412 return String::new();
413 }
414 unsafe { CStr::from_ptr(ptr) }
415 .to_string_lossy()
416 .into_owned()
417 }
418
419 pub fn set_restore_token(&self, token: &str) -> MaaResult<()> {
421 let c_token = CString::new(token)?;
422 unsafe { sys::MaaToolkitPortalHelperSetRestoreToken(self.handle, c_token.as_ptr()) };
423 Ok(())
424 }
425}
426
427impl Drop for PortalHelper {
428 fn drop(&mut self) {
429 unsafe { sys::MaaToolkitPortalHelperDestroy(self.handle) };
430 }
431}