Skip to main content

Controller

Struct Controller 

Source
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

Source

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 executable
  • address - Device address (e.g., “127.0.0.1:5555” or “emulator-5554”)
  • config - JSON configuration string for advanced options
  • agent_path - Optional path to MaaAgentBinary
Examples found in repository?
examples/main.rs (lines 259-264)
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}
Source

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.

Source

pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self>

Create a new PlayCover controller for iOS app control on macOS.

Source

pub fn new_custom<T: CustomControllerCallback + 'static>( callback: T, ) -> MaaResult<Self>

Create a custom controller with user-defined callbacks.

Source

pub fn post_click(&self, x: i32, y: i32) -> MaaResult<MaaId>

Post a click action at the specified coordinates.

Examples found in repository?
examples/main.rs (line 302)
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}
Source

pub fn post_screencap(&self) -> MaaResult<MaaId>

Post a screenshot capture request.

Examples found in repository?
examples/main.rs (line 297)
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}
Source

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 coordinates
  • contact - Contact/finger index (for multi-touch)
  • pressure - Touch pressure (1 = normal)
Source

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 coordinates
  • x2, y2 - End coordinates
  • duration - Swipe duration in milliseconds
Source

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)
Source

pub fn post_press(&self, keycode: i32) -> MaaResult<MaaId>

👎Deprecated: Use post_click_key instead

Alias for post_click_key.

Source

pub fn post_input_text(&self, text: &str) -> MaaResult<MaaId>

Post a text input action.

§Arguments
  • text - Text to input
Examples found in repository?
examples/main.rs (line 313)
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}
Source

pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<MaaId>

Post a shell command execution (ADB only).

§Arguments
  • cmd - Shell command to execute
  • timeout - Timeout in milliseconds
Source

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 index
  • x, y - Touch coordinates
  • pressure - Touch pressure
Source

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 index
  • x, y - New touch coordinates
  • pressure - Touch pressure
Source

pub fn post_touch_up(&self, contact: i32) -> MaaResult<MaaId>

Post a touch up event.

§Arguments
  • contact - Contact/finger index to release
Source

pub fn raw(&self) -> *mut MaaController

Returns the underlying raw controller handle.

Source

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?
examples/main.rs (line 292)
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}
Source

pub fn connected(&self) -> bool

Returns true if the controller is connected to the device.

Examples found in repository?
examples/main.rs (line 294)
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}
Source

pub fn uuid(&self) -> MaaResult<String>

Gets the unique identifier (UUID) of the connected device.

Source

pub fn resolution(&self) -> MaaResult<(i32, i32)>

Gets the device screen resolution as (width, height).

Examples found in repository?
examples/main.rs (line 318)
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}
Source

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 coordinates
  • x2, y2 - End coordinates
  • duration - Swipe duration in milliseconds
  • contact - Contact/finger index
  • pressure - Touch pressure
Source

pub fn post_key_down(&self, keycode: i32) -> MaaResult<MaaId>

Post a key down event.

Source

pub fn post_key_up(&self, keycode: i32) -> MaaResult<MaaId>

Post a key up event.

Source

pub fn post_start_app(&self, intent: &str) -> MaaResult<MaaId>

Start an application.

§Arguments
  • intent - Package name or activity (ADB), app identifier (Win32)
Source

pub fn post_stop_app(&self, intent: &str) -> MaaResult<MaaId>

Stop an application.

§Arguments
  • intent - Package name (ADB)
Source

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)
Source

pub fn cached_image(&self) -> MaaResult<MaaImageBuffer>

Gets the most recently captured screenshot.

Source

pub fn shell_output(&self) -> MaaResult<String>

Gets the output from the most recent shell command (ADB only).

Source

pub fn status(&self, ctrl_id: MaaId) -> MaaStatus

Gets the status of a controller operation.

Source

pub fn wait(&self, ctrl_id: MaaId) -> MaaStatus

Blocks until a controller operation completes.

Examples found in repository?
examples/main.rs (line 293)
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}
Source

pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()>

Sets the target long side for screenshot scaling.

Source

pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()>

Sets the target short side for screenshot scaling.

Source

pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()>

Sets whether to use raw (unscaled) screenshot resolution.

Source

pub fn new_dbg( read_path: &str, write_path: &str, dbg_type: MaaDbgControllerType, config: &str, ) -> MaaResult<Self>

Source

pub fn new_gamepad( hwnd: *mut c_void, gamepad_type: GamepadType, screencap_method: Win32ScreencapMethod, ) -> MaaResult<Self>

Create a virtual gamepad controller (Windows only).

Source

pub fn add_sink<F>(&self, callback: F) -> MaaResult<MaaSinkId>
where F: Fn(&str, &str) + Send + Sync + 'static,

Returns sink_id for later removal. Callback lifetime managed by caller.

Source

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.

Source

pub fn remove_sink(&self, sink_id: MaaSinkId)

Source

pub fn clear_sinks(&self)

Trait Implementations§

Source§

impl Clone for Controller

Source§

fn clone(&self) -> Controller

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Controller

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Send for Controller

Source§

impl Sync for Controller

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.