pub struct Controller { /* private fields */ }Expand description
Device controller interface.
Handles interaction with the target device, including:
- Input events (click, swipe, key press)
- Screen capture
- App management (start/stop)
- Connection management
See also: AdbControllerBuilder for advanced ADB configuration.
Implementations§
Source§impl Controller
impl Controller
Sourcepub fn new_adb(
adb_path: &str,
address: &str,
config: &str,
agent_path: Option<&str>,
) -> MaaResult<Self>
pub fn new_adb( adb_path: &str, address: &str, config: &str, agent_path: Option<&str>, ) -> MaaResult<Self>
Create a new ADB controller for Android device control.
§Arguments
adb_path- Path to the ADB executableaddress- Device address (e.g., “127.0.0.1:5555” or “emulator-5554”)config- JSON configuration string for advanced optionsagent_path- Optional path to MaaAgentBinary
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn new_win32(
hwnd: *mut c_void,
screencap_method: MaaWin32ScreencapMethod,
mouse_method: MaaWin32InputMethod,
keyboard_method: MaaWin32InputMethod,
) -> MaaResult<Self>
pub fn new_win32( hwnd: *mut c_void, screencap_method: MaaWin32ScreencapMethod, mouse_method: MaaWin32InputMethod, keyboard_method: MaaWin32InputMethod, ) -> MaaResult<Self>
Create a new Win32 controller for Windows window control.
Sourcepub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self>
pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self>
Create a new PlayCover controller for iOS app control on macOS.
Sourcepub fn new_custom<T: CustomControllerCallback + 'static>(
callback: T,
) -> MaaResult<Self>
pub fn new_custom<T: CustomControllerCallback + 'static>( callback: T, ) -> MaaResult<Self>
Create a custom controller with user-defined callbacks.
Sourcepub fn post_click(&self, x: i32, y: i32) -> MaaResult<MaaId>
pub fn post_click(&self, x: i32, y: i32) -> MaaResult<MaaId>
Post a click action at the specified coordinates.
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn post_screencap(&self) -> MaaResult<MaaId>
pub fn post_screencap(&self) -> MaaResult<MaaId>
Post a screenshot capture request.
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn post_click_v2(
&self,
x: i32,
y: i32,
contact: i32,
pressure: i32,
) -> MaaResult<MaaId>
pub fn post_click_v2( &self, x: i32, y: i32, contact: i32, pressure: i32, ) -> MaaResult<MaaId>
Post a click action with contact and pressure parameters.
§Arguments
x,y- Click coordinatescontact- Contact/finger index (for multi-touch)pressure- Touch pressure (1 = normal)
Sourcepub fn post_swipe(
&self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
duration: i32,
) -> MaaResult<MaaId>
pub fn post_swipe( &self, x1: i32, y1: i32, x2: i32, y2: i32, duration: i32, ) -> MaaResult<MaaId>
Post a swipe action from one point to another.
§Arguments
x1,y1- Start coordinatesx2,y2- End coordinatesduration- Swipe duration in milliseconds
Sourcepub fn post_click_key(&self, keycode: i32) -> MaaResult<MaaId>
pub fn post_click_key(&self, keycode: i32) -> MaaResult<MaaId>
Post a key click action.
§Arguments
keycode- Virtual key code (ADB keycode for Android, VK for Win32)
Sourcepub fn post_press(&self, keycode: i32) -> MaaResult<MaaId>
👎Deprecated: Use post_click_key instead
pub fn post_press(&self, keycode: i32) -> MaaResult<MaaId>
Alias for post_click_key.
Sourcepub fn post_input_text(&self, text: &str) -> MaaResult<MaaId>
pub fn post_input_text(&self, text: &str) -> MaaResult<MaaId>
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<MaaId>
pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<MaaId>
Post a shell command execution (ADB only).
§Arguments
cmd- Shell command to executetimeout- Timeout in milliseconds
Sourcepub fn post_touch_down(
&self,
contact: i32,
x: i32,
y: i32,
pressure: i32,
) -> MaaResult<MaaId>
pub fn post_touch_down( &self, contact: i32, x: i32, y: i32, pressure: i32, ) -> MaaResult<MaaId>
Post a touch down event.
§Arguments
contact- Contact/finger indexx,y- Touch coordinatespressure- Touch pressure
Sourcepub fn post_touch_move(
&self,
contact: i32,
x: i32,
y: i32,
pressure: i32,
) -> MaaResult<MaaId>
pub fn post_touch_move( &self, contact: i32, x: i32, y: i32, pressure: i32, ) -> MaaResult<MaaId>
Post a touch move event.
§Arguments
contact- Contact/finger indexx,y- New touch coordinatespressure- Touch pressure
Sourcepub fn post_touch_up(&self, contact: i32) -> MaaResult<MaaId>
pub fn post_touch_up(&self, contact: i32) -> MaaResult<MaaId>
Sourcepub fn raw(&self) -> *mut MaaController
pub fn raw(&self) -> *mut MaaController
Returns the underlying raw controller handle.
Sourcepub fn post_connection(&self) -> MaaResult<MaaId>
pub fn post_connection(&self) -> MaaResult<MaaId>
Post a connection request to the device.
Returns a job ID that can be used with wait to block until connected.
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn connected(&self) -> bool
pub fn connected(&self) -> bool
Returns true if the controller is connected to the device.
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn uuid(&self) -> MaaResult<String>
pub fn uuid(&self) -> MaaResult<String>
Gets the unique identifier (UUID) of the connected device.
Sourcepub fn resolution(&self) -> MaaResult<(i32, i32)>
pub fn resolution(&self) -> MaaResult<(i32, i32)>
Gets the device screen resolution as (width, height).
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn post_swipe_v2(
&self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
duration: i32,
contact: i32,
pressure: i32,
) -> MaaResult<MaaId>
pub fn post_swipe_v2( &self, x1: i32, y1: i32, x2: i32, y2: i32, duration: i32, contact: i32, pressure: i32, ) -> MaaResult<MaaId>
Post a swipe action with contact and pressure parameters.
§Arguments
x1,y1- Start coordinatesx2,y2- End coordinatesduration- Swipe duration in millisecondscontact- Contact/finger indexpressure- Touch pressure
Sourcepub fn post_key_down(&self, keycode: i32) -> MaaResult<MaaId>
pub fn post_key_down(&self, keycode: i32) -> MaaResult<MaaId>
Post a key down event.
Sourcepub fn post_key_up(&self, keycode: i32) -> MaaResult<MaaId>
pub fn post_key_up(&self, keycode: i32) -> MaaResult<MaaId>
Post a key up event.
Sourcepub fn post_start_app(&self, intent: &str) -> MaaResult<MaaId>
pub fn post_start_app(&self, intent: &str) -> MaaResult<MaaId>
Sourcepub fn post_stop_app(&self, intent: &str) -> MaaResult<MaaId>
pub fn post_stop_app(&self, intent: &str) -> MaaResult<MaaId>
Sourcepub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<MaaId>
pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<MaaId>
Post a scroll action (Win32 only).
§Arguments
dx- Horizontal scroll delta (positive = right)dy- Vertical scroll delta (positive = down)
Sourcepub fn cached_image(&self) -> MaaResult<MaaImageBuffer>
pub fn cached_image(&self) -> MaaResult<MaaImageBuffer>
Gets the most recently captured screenshot.
Sourcepub fn shell_output(&self) -> MaaResult<String>
pub fn shell_output(&self) -> MaaResult<String>
Gets the output from the most recent shell command (ADB only).
Sourcepub fn wait(&self, ctrl_id: MaaId) -> MaaStatus
pub fn wait(&self, ctrl_id: MaaId) -> MaaStatus
Blocks until a controller operation completes.
Examples found in repository?
201fn main() -> Result<(), Box<dyn std::error::Error>> {
202 println!("=== MaaFramework Rust SDK Demo ===\n");
203
204 // -------------------------------------------------------------------------
205 // 1. Initialize Toolkit
206 // -------------------------------------------------------------------------
207 let user_path = "./";
208 Toolkit::init_option(user_path, "{}")?;
209 println!("[1] Toolkit initialized with user_path: {}", user_path);
210
211 // -------------------------------------------------------------------------
212 // 2. Device Discovery
213 // -------------------------------------------------------------------------
214
215 // ADB devices
216 println!("\n[2] Scanning for ADB devices...");
217 let adb_devices = Toolkit::find_adb_devices()?;
218 if adb_devices.is_empty() {
219 println!(" No ADB device found.");
220 } else {
221 for device in &adb_devices {
222 println!(" Found: {} ({})", device.name, device.address);
223 }
224 }
225
226 // Win32 windows (Windows only)
227 #[cfg(target_os = "windows")]
228 {
229 println!("\n Scanning for desktop windows...");
230 match Toolkit::find_desktop_windows() {
231 Ok(windows) => {
232 if windows.is_empty() {
233 println!(" No window found.");
234 } else {
235 for (i, win) in windows.iter().take(3).enumerate() {
236 println!(" [{}] hwnd={:?}, class={}", i, win.hwnd, win.class_name);
237 }
238 if windows.len() > 3 {
239 println!(" ... and {} more", windows.len() - 3);
240 }
241 }
242 }
243 Err(e) => println!(" Failed to find windows: {}", e),
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // 3. Create Controller (choose one)
249 // -------------------------------------------------------------------------
250 println!("\n[3] Creating controller...");
251
252 let controller: Option<Controller>;
253
254 // Option A: ADB Controller
255 #[cfg(feature = "adb")]
256 if let Some(device) = adb_devices.first() {
257 println!(" Using ADB device: {}", device.name);
258 let config_str = serde_json::to_string(&device.config)?;
259 controller = Some(Controller::new_adb(
260 device.adb_path.to_str().unwrap(),
261 &device.address,
262 &config_str,
263 None,
264 )?);
265 } else {
266 controller = None;
267 }
268
269 #[cfg(not(feature = "adb"))]
270 {
271 controller = None;
272 }
273
274 // Option B: Win32 Controller (uncomment to use)
275 // #[cfg(target_os = "windows")]
276 // if let Some(window) = windows.first() {
277 // controller = Some(Controller::new_win32(
278 // window.hwnd as isize,
279 // common::Win32ScreencapMethod::FramePool as i32,
280 // common::Win32InputMethod::PostMessage as i32,
281 // common::Win32InputMethod::PostMessage as i32,
282 // )?);
283 // }
284
285 // -------------------------------------------------------------------------
286 // 4. Controller Operations
287 // -------------------------------------------------------------------------
288 if let Some(ref ctrl) = controller {
289 println!("\n[4] Controller operations...");
290
291 // Connect
292 let conn_id = ctrl.post_connection()?;
293 ctrl.wait(conn_id);
294 println!(" Connected: {}", ctrl.connected());
295
296 // Screenshot
297 let cap_id = ctrl.post_screencap()?;
298 ctrl.wait(cap_id);
299 println!(" Screenshot captured");
300
301 // Click (sync)
302 let click_id = ctrl.post_click(114, 514)?;
303 ctrl.wait(click_id);
304 println!(" Clicked at (114, 514)");
305
306 // Click (async) - post now, wait later
307 let click_id2 = ctrl.post_click(514, 114)?;
308 // ... do other work here ...
309 ctrl.wait(click_id2);
310 println!(" Async click completed");
311
312 // Input text
313 let input_id = ctrl.post_input_text("Hello MAA!")?;
314 ctrl.wait(input_id);
315 println!(" Text input sent");
316
317 // Get resolution
318 if let Ok((w, h)) = ctrl.resolution() {
319 println!(" Resolution: {}x{}", w, h);
320 }
321 } else {
322 println!("\n[4] Skipping controller operations (no device available)");
323 }
324
325 // -------------------------------------------------------------------------
326 // 5. Resource Setup
327 // -------------------------------------------------------------------------
328 println!("\n[5] Setting up resource...");
329
330 let resource = Resource::new()?;
331
332 // Register custom recognition and action
333 resource.register_custom_recognition("MyRecognition", Box::new(MyRecognition))?;
334 resource.register_custom_action("MyAction", Box::new(MyAction))?;
335 println!(" Registered: MyRecognition, MyAction");
336
337 // List registered components
338 let reco_list = resource.custom_recognition_list()?;
339 let action_list = resource.custom_action_list()?;
340 println!(" Recognition list: {:?}", reco_list);
341 println!(" Action list: {:?}", action_list);
342
343 // Load resource bundle
344 let resource_path = "sample/resource";
345 println!(" Loading resource from: {}", resource_path);
346 match resource.post_bundle(resource_path) {
347 Ok(job) => {
348 let status = job.wait();
349 println!(" Load status: {:?}", status);
350 }
351 Err(e) => println!(" Load failed: {} (OK for demo)", e),
352 }
353
354 // Add event sink
355 resource.add_sink(|msg, details| {
356 println!(
357 " [ResourceEvent] {}: {}",
358 msg,
359 &details[..details.len().min(50)]
360 );
361 })?;
362
363 // -------------------------------------------------------------------------
364 // 6. Tasker Setup and Execution
365 // -------------------------------------------------------------------------
366 println!("\n[6] Setting up tasker...");
367
368 let tasker = Tasker::new()?;
369
370 // Bind resource
371 tasker.bind_resource(&resource)?;
372 println!(" Resource bound");
373
374 // Bind controller (if available)
375 if let Some(ref ctrl) = controller {
376 tasker.bind_controller(ctrl)?;
377 println!(" Controller bound");
378 }
379
380 // Check initialization
381 if tasker.inited() {
382 println!(" Tasker initialized successfully!");
383
384 // Add event sink
385 tasker.add_sink(|msg, details| {
386 println!(
387 " [TaskerEvent] {}: {}",
388 msg,
389 &details[..details.len().min(50)]
390 );
391 })?;
392
393 // Execute task with pipeline override
394 let pipeline_override = r#"{
395 "MyCustomEntry": {
396 "recognition": "Custom",
397 "custom_recognition": "MyRecognition",
398 "action": "Custom",
399 "custom_action": "MyAction"
400 }
401 }"#;
402
403 println!("\n[7] Executing task...");
404 match tasker.post_task("MyCustomEntry", pipeline_override) {
405 Ok(job) => {
406 let status = job.wait();
407 println!(" Task status: {:?}", status);
408
409 if let Ok(Some(detail)) = job.get(false) {
410 println!(" Entry: {}", detail.entry);
411 println!(" Nodes executed: {}", detail.nodes.len());
412
413 for node_opt in detail.nodes {
414 if let Some(node) = node_opt {
415 if let Some(reco) = node.recognition {
416 println!(
417 " Node: {}, Algo: {:?}",
418 node.node_name, reco.algorithm
419 );
420 }
421 }
422 }
423 }
424 }
425 Err(e) => println!(" Task failed: {}", e),
426 }
427 } else {
428 println!(" Tasker not fully initialized (missing controller binding)");
429 }
430
431 // -------------------------------------------------------------------------
432 // 8. Cleanup
433 // -------------------------------------------------------------------------
434 println!("\n[8] Demo completed!");
435 println!(" - Toolkit, Controller, Resource, Tasker all demonstrated");
436 println!(" - Custom Recognition and Action registered and explained");
437 println!(" - Context API documented in custom component implementations");
438
439 Ok(())
440}Sourcepub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()>
pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()>
Sets the target long side for screenshot scaling.
Sourcepub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()>
pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()>
Sets the target short side for screenshot scaling.
Sourcepub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()>
pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()>
Sets whether to use raw (unscaled) screenshot resolution.
pub fn new_dbg( read_path: &str, write_path: &str, dbg_type: MaaDbgControllerType, config: &str, ) -> MaaResult<Self>
Sourcepub fn new_gamepad(
hwnd: *mut c_void,
gamepad_type: GamepadType,
screencap_method: Win32ScreencapMethod,
) -> MaaResult<Self>
pub fn new_gamepad( hwnd: *mut c_void, gamepad_type: GamepadType, screencap_method: Win32ScreencapMethod, ) -> MaaResult<Self>
Create a virtual gamepad controller (Windows only).
Sourcepub fn add_sink<F>(&self, callback: F) -> MaaResult<MaaSinkId>
pub fn add_sink<F>(&self, callback: F) -> MaaResult<MaaSinkId>
Returns sink_id for later removal. Callback lifetime managed by caller.
Sourcepub fn add_event_sink(&self, sink: Box<dyn EventSink>) -> MaaResult<MaaSinkId>
pub fn add_event_sink(&self, sink: Box<dyn EventSink>) -> MaaResult<MaaSinkId>
Register a strongly-typed event sink.
This method registers an implementation of the EventSink trait
to receive structured notifications from this controller.
§Arguments
sink- The event sink implementation (must be boxed).
§Returns
A MaaSinkId which can be used to manually remove the sink later via remove_sink.
The sink will be automatically unregistered and dropped when the Controller is dropped.
pub fn remove_sink(&self, sink_id: MaaSinkId)
pub fn clear_sinks(&self)
Trait Implementations§
Source§impl Clone for Controller
impl Clone for Controller
Source§fn clone(&self) -> Controller
fn clone(&self) -> Controller
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more