Skip to main content

maa_framework/
toolkit.rs

1//! Device discovery and configuration utilities.
2
3use 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/// Information about a connected ADB device.
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct AdbDevice {
13    /// Device display name.
14    pub name: String,
15    /// Path to the ADB executable.
16    pub adb_path: PathBuf,
17    /// Device address (e.g., "127.0.0.1:5555").
18    pub address: String,
19    /// Supported screencap methods (bitflags).
20    pub screencap_methods: u64,
21    /// Supported input methods (bitflags).
22    pub input_methods: u64,
23    /// Device configuration as JSON.
24    pub config: serde_json::Value,
25}
26
27/// Information about a desktop window (Win32).
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct DesktopWindow {
30    /// Window handle (HWND).
31    pub hwnd: usize,
32    /// Window class name.
33    pub class_name: String,
34    /// Window title.
35    pub window_name: String,
36}
37
38/// macOS system permission types used by toolkit helpers.
39#[repr(i32)]
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41pub enum MacOSPermission {
42    /// Screen recording / screen capture permission.
43    ScreenCapture = sys::MaaMacOSPermissionEnum_MaaMacOSPermissionScreenCapture as i32,
44    /// Accessibility permission for input simulation.
45    Accessibility = sys::MaaMacOSPermissionEnum_MaaMacOSPermissionAccessibility as i32,
46}
47
48/// Toolkit utilities for device discovery and configuration.
49pub struct Toolkit;
50
51static AGENT_SERVER_INIT_OPTION_WARNING: Once = Once::new();
52
53impl Toolkit {
54    #[inline]
55    fn unsupported(api: &str) -> MaaError {
56        MaaError::UnsupportedInAgentServer(api.to_string())
57    }
58
59    fn maybe_warn_init_option_in_agent_server() {
60        if std::env::var_os("MAA_RUST_WARN_AGENTSERVER_TOOLKIT_INIT").is_none() {
61            return;
62        }
63
64        AGENT_SERVER_INIT_OPTION_WARNING.call_once(|| {
65            eprintln!(
66                "Warning: Toolkit::init_option is deprecated in AgentServer; only log_dir is applied."
67            );
68        });
69    }
70
71    /// Initialize MAA framework options.
72    ///
73    /// # Arguments
74    /// * `user_path` - Path to user data directory
75    /// * `default_config` - Default configuration JSON string
76    pub fn init_option(user_path: &str, default_config: &str) -> MaaResult<()> {
77        if crate::is_agent_server_context() {
78            let _ = default_config;
79            Self::maybe_warn_init_option_in_agent_server();
80            let log_dir = Path::new(user_path).join("debug");
81            return crate::configure_logging(log_dir.to_string_lossy().as_ref());
82        }
83
84        let c_path = CString::new(user_path)?;
85        let c_config = CString::new(default_config)?;
86        let ret = unsafe { sys::MaaToolkitConfigInitOption(c_path.as_ptr(), c_config.as_ptr()) };
87        common::check_bool(ret)
88    }
89
90    /// Find connected ADB devices.
91    ///
92    /// Scans for all known Android emulators and connected ADB devices.
93    ///
94    /// # Returns
95    /// List of discovered ADB devices with their configurations.
96    pub fn find_adb_devices() -> MaaResult<Vec<AdbDevice>> {
97        if crate::is_agent_server_context() {
98            return Err(Self::unsupported("Toolkit::find_adb_devices"));
99        }
100        Self::find_adb_devices_impl(None)
101    }
102
103    /// Find connected ADB devices using a specific ADB binary.
104    ///
105    /// # Arguments
106    /// * `adb_path` - Path to the ADB binary to use for discovery
107    ///
108    /// # Returns
109    /// List of discovered ADB devices with their configurations.
110    pub fn find_adb_devices_with_adb(adb_path: &str) -> MaaResult<Vec<AdbDevice>> {
111        if crate::is_agent_server_context() {
112            return Err(Self::unsupported("Toolkit::find_adb_devices_with_adb"));
113        }
114        Self::find_adb_devices_impl(Some(adb_path))
115    }
116
117    fn find_adb_devices_impl(specified_adb: Option<&str>) -> MaaResult<Vec<AdbDevice>> {
118        let list = unsafe { sys::MaaToolkitAdbDeviceListCreate() };
119        if list.is_null() {
120            return Err(MaaError::NullPointer);
121        }
122
123        let _guard = AdbDeviceListGuard(list);
124
125        unsafe {
126            let ret = if let Some(adb_path) = specified_adb {
127                let c_path = CString::new(adb_path)?;
128                sys::MaaToolkitAdbDeviceFindSpecified(c_path.as_ptr(), list)
129            } else {
130                sys::MaaToolkitAdbDeviceFind(list)
131            };
132            common::check_bool(ret)?;
133
134            let count = sys::MaaToolkitAdbDeviceListSize(list);
135            let mut devices = Vec::with_capacity(count as usize);
136
137            for i in 0..count {
138                let device_ptr = sys::MaaToolkitAdbDeviceListAt(list, i);
139                if device_ptr.is_null() {
140                    continue;
141                }
142
143                let name = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetName(device_ptr))
144                    .to_string_lossy()
145                    .into_owned();
146
147                let adb_path_str = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAdbPath(device_ptr))
148                    .to_string_lossy()
149                    .into_owned();
150
151                let address = CStr::from_ptr(sys::MaaToolkitAdbDeviceGetAddress(device_ptr))
152                    .to_string_lossy()
153                    .into_owned();
154
155                let screencap_methods =
156                    sys::MaaToolkitAdbDeviceGetScreencapMethods(device_ptr) as u64;
157                let input_methods = sys::MaaToolkitAdbDeviceGetInputMethods(device_ptr) as u64;
158
159                let config_str =
160                    CStr::from_ptr(sys::MaaToolkitAdbDeviceGetConfig(device_ptr)).to_string_lossy();
161                let config = serde_json::from_str(&config_str).unwrap_or(serde_json::Value::Null);
162
163                devices.push(AdbDevice {
164                    name,
165                    adb_path: PathBuf::from(adb_path_str),
166                    address,
167                    screencap_methods,
168                    input_methods,
169                    config,
170                });
171            }
172            Ok(devices)
173        }
174    }
175
176    /// Find all desktop windows (Win32 only).
177    ///
178    /// # Returns
179    /// List of visible desktop windows.
180    pub fn find_desktop_windows() -> MaaResult<Vec<DesktopWindow>> {
181        if crate::is_agent_server_context() {
182            return Err(Self::unsupported("Toolkit::find_desktop_windows"));
183        }
184
185        let list = unsafe { sys::MaaToolkitDesktopWindowListCreate() };
186        if list.is_null() {
187            return Err(MaaError::NullPointer);
188        }
189
190        let _guard = DesktopWindowListGuard(list);
191
192        unsafe {
193            let ret = sys::MaaToolkitDesktopWindowFindAll(list);
194            common::check_bool(ret)?;
195
196            let count = sys::MaaToolkitDesktopWindowListSize(list);
197            let mut windows = Vec::with_capacity(count as usize);
198
199            for i in 0..count {
200                let win_ptr = sys::MaaToolkitDesktopWindowListAt(list, i);
201                if win_ptr.is_null() {
202                    continue;
203                }
204
205                let hwnd = sys::MaaToolkitDesktopWindowGetHandle(win_ptr) as usize;
206
207                let class_name = CStr::from_ptr(sys::MaaToolkitDesktopWindowGetClassName(win_ptr))
208                    .to_string_lossy()
209                    .into_owned();
210
211                let window_name =
212                    CStr::from_ptr(sys::MaaToolkitDesktopWindowGetWindowName(win_ptr))
213                        .to_string_lossy()
214                        .into_owned();
215
216                windows.push(DesktopWindow {
217                    hwnd,
218                    class_name,
219                    window_name,
220                });
221            }
222            Ok(windows)
223        }
224    }
225
226    /// Check whether the current process has the specified macOS permission.
227    pub fn macos_check_permission(permission: MacOSPermission) -> MaaResult<bool> {
228        if crate::is_agent_server_context() {
229            return Err(Self::unsupported("Toolkit::macos_check_permission"));
230        }
231
232        let ret =
233            unsafe { sys::MaaToolkitMacOSCheckPermission(permission as sys::MaaMacOSPermission) };
234        Ok(ret != 0)
235    }
236
237    /// Request the specified macOS permission from the system.
238    ///
239    /// A successful return means the request API call succeeded. It does not
240    /// necessarily mean the user has already granted the permission.
241    pub fn macos_request_permission(permission: MacOSPermission) -> MaaResult<bool> {
242        if crate::is_agent_server_context() {
243            return Err(Self::unsupported("Toolkit::macos_request_permission"));
244        }
245
246        let ret =
247            unsafe { sys::MaaToolkitMacOSRequestPermission(permission as sys::MaaMacOSPermission) };
248        Ok(ret != 0)
249    }
250
251    /// Open the corresponding macOS settings page for the permission.
252    pub fn macos_reveal_permission_settings(permission: MacOSPermission) -> MaaResult<bool> {
253        if crate::is_agent_server_context() {
254            return Err(Self::unsupported(
255                "Toolkit::macos_reveal_permission_settings",
256            ));
257        }
258
259        let ret = unsafe {
260            sys::MaaToolkitMacOSRevealPermissionSettings(permission as sys::MaaMacOSPermission)
261        };
262        Ok(ret != 0)
263    }
264}
265
266struct AdbDeviceListGuard(*mut sys::MaaToolkitAdbDeviceList);
267impl Drop for AdbDeviceListGuard {
268    fn drop(&mut self) {
269        unsafe { sys::MaaToolkitAdbDeviceListDestroy(self.0) }
270    }
271}
272
273struct DesktopWindowListGuard(*mut sys::MaaToolkitDesktopWindowList);
274impl Drop for DesktopWindowListGuard {
275    fn drop(&mut self) {
276        unsafe { sys::MaaToolkitDesktopWindowListDestroy(self.0) }
277    }
278}