pub struct TaskJob<'a, T> { /* private fields */ }Expand description
Task job handle with extended capabilities.
Inherits from JobWithResult, additionally providing task-specific operations.
Implementations§
Source§impl<'a, T> TaskJob<'a, T>
impl<'a, T> TaskJob<'a, T>
Sourcepub fn new(
job: JobWithResult<'a, T>,
override_fn: OverridePipelineFn<'a>,
) -> Self
pub fn new( job: JobWithResult<'a, T>, override_fn: OverridePipelineFn<'a>, ) -> Self
Create a new TaskJob.
Methods from Deref<Target = JobWithResult<'a, T>>§
Sourcepub fn get(&self, wait: bool) -> MaaResult<Option<T>>
pub fn get(&self, wait: bool) -> MaaResult<Option<T>>
Get the operation result.
§Arguments
wait- Iftrue, blocks until the operation completes before getting the result
Examples found in repository?
examples/main.rs (line 134)
37 fn analyze(
38 &self,
39 context: &Context,
40 _task_id: sys::MaaTaskId,
41 node_name: &str,
42 custom_recognition_name: &str,
43 custom_recognition_param: &str,
44 _image: &maa_framework::buffer::MaaImageBuffer,
45 _roi: &maa_framework::common::Rect,
46 ) -> Option<(maa_framework::common::Rect, String)> {
47 println!(
48 "[MyRecognition] analyze called: node={}, name={}, param={}",
49 node_name, custom_recognition_name, custom_recognition_param
50 );
51
52 // --- Context API Demo ---
53
54 // 1. Run sub-pipeline recognition with override
55 let pp_override = r#"{"MyCustomOCR": {"recognition": "OCR", "roi": [100, 100, 200, 300]}}"#;
56 if let Ok(img_buf) = buffer::MaaImageBuffer::new() {
57 let reco_id = context.run_recognition("MyCustomOCR", pp_override, &img_buf);
58 println!(" run_recognition result: {:?}", reco_id);
59 }
60
61 // 2. Take a new screenshot via tasker's controller
62 let tasker_ptr = context.tasker_handle();
63 if !tasker_ptr.is_null() {
64 // Note: In real usage, you would use the Tasker's controller reference
65 println!(" tasker handle available for controller access");
66 }
67
68 // 3. Async click - post now, will wait later
69 // (In real code, you'd get controller from context.tasker)
70 println!(" [Demo] Would post async click at (10, 20)");
71
72 // 4. Override pipeline for all subsequent operations in this context
73 let _ = context.override_pipeline(r#"{"MyCustomOCR": {"roi": [1, 1, 114, 514]}}"#);
74
75 // 5. Check if tasker is stopping - IMPORTANT for responsive cancellation
76 // Note: Context provides tasker_handle() which returns a raw pointer.
77 // In production code, you should wrap this or use the Tasker instance directly.
78 // Here we demonstrate the raw API call for completeness.
79 let tasker_ptr = context.tasker_handle();
80 if !tasker_ptr.is_null() {
81 // Using sys:: directly requires unsafe - this matches C API usage
82 let is_stopping = unsafe { sys::MaaTaskerStopping(tasker_ptr) != 0 };
83 if is_stopping {
84 println!(" Task is stopping, returning early!");
85 return Some((
86 common::Rect {
87 x: 0,
88 y: 0,
89 width: 0,
90 height: 0,
91 },
92 r#"{"status": "Task Stopped"}"#.to_string(),
93 ));
94 }
95 }
96
97 // 6. Wait for the async click to complete
98 println!(" [Demo] Async click would complete here");
99
100 // 7. Clone context for independent operations (modifications won't affect original)
101 if let Ok(new_ctx) = context.clone_context() {
102 let _ = new_ctx.override_pipeline(r#"{"MyCustomOCR": {"roi": [100, 200, 300, 400]}}"#);
103
104 // Run recognition and use the result
105 if let Ok(img_buf) = buffer::MaaImageBuffer::new() {
106 let reco_id = new_ctx.run_recognition("MyCustomOCR", "{}", &img_buf);
107
108 // If recognition succeeded, get the tasker to retrieve details
109 if reco_id.is_ok() {
110 // In a real scenario, you would:
111 // 1. Get recognition detail from tasker
112 // 2. Check if it hit
113 // 3. Use the box coordinates to click
114 println!(" [Demo] Would get reco detail and click on result box");
115
116 // Example of clicking on recognition result box:
117 // if let Ok(Some(detail)) = tasker.get_recognition_detail(reco_id) {
118 // if detail.hit {
119 // let box_rect = detail.box_rect;
120 // controller.post_click(box_rect.x, box_rect.y).wait();
121 // }
122 // }
123 }
124 }
125 // new_ctx changes don't affect original context
126 }
127
128 // 8. Get current task ID
129 let task_id = context.task_id();
130 println!(" current task_id: {}", task_id);
131
132 // 9. Get task job for result retrieval
133 let task_job = context.get_task_job();
134 if let Ok(Some(detail)) = task_job.get(false) {
135 println!(" task entry: {}", detail.entry);
136 }
137
138 // Return recognition result: bounding box + detail JSON
139 Some((
140 common::Rect {
141 x: 0,
142 y: 0,
143 width: 100,
144 height: 100,
145 },
146 r#"{"message": "Hello World!"}"#.to_string(),
147 ))
148 }
149}
150
151// ============================================================================
152// Custom Action Implementation
153// ============================================================================
154
155/// Example custom action.
156///
157/// Actions are executed after a successful recognition.
158struct MyAction;
159
160impl CustomAction for MyAction {
161 fn run(
162 &self,
163 context: &Context,
164 _task_id: sys::MaaTaskId,
165 node_name: &str,
166 custom_action_name: &str,
167 custom_action_param: &str,
168 reco_id: sys::MaaRecoId,
169 box_rect: &maa_framework::common::Rect,
170 ) -> bool {
171 println!(
172 "[MyAction] run called: node={}, name={}, param={}",
173 node_name, custom_action_name, custom_action_param
174 );
175 println!(
176 " reco_id={}, box=({}, {}, {}, {})",
177 reco_id, box_rect.x, box_rect.y, box_rect.width, box_rect.height
178 );
179
180 // You can use Context API here too
181 let _ = context.run_action(
182 "Click",
183 "{}",
184 &common::Rect {
185 x: 114,
186 y: 514,
187 width: 100,
188 height: 100,
189 },
190 "{}",
191 );
192
193 true // Return true for success, false for failure
194 }
195}
196
197// ============================================================================
198// Main Demo
199// ============================================================================
200
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}Methods from Deref<Target = Job<'a>>§
Sourcepub fn wait(&self) -> MaaStatus
pub fn wait(&self) -> MaaStatus
Block until the operation completes.
Examples found in repository?
examples/main.rs (line 348)
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}Trait Implementations§
Auto Trait Implementations§
impl<'a, T> Freeze for TaskJob<'a, T>
impl<'a, T> !RefUnwindSafe for TaskJob<'a, T>
impl<'a, T> Send for TaskJob<'a, T>
impl<'a, T> Sync for TaskJob<'a, T>
impl<'a, T> Unpin for TaskJob<'a, T>
impl<'a, T> !UnwindSafe for TaskJob<'a, T>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more