1use crate::{MaaError, MaaResult, common, sys};
4use std::collections::HashMap;
5use std::ffi::CString;
6use std::os::raw::c_void;
7use std::ptr::NonNull;
8use std::sync::{Arc, Mutex};
9
10#[derive(Clone)]
20pub struct Controller {
21 inner: Arc<ControllerInner>,
22}
23
24struct ControllerInner {
25 handle: NonNull<sys::MaaController>,
26 owns_handle: bool,
27 callbacks: Mutex<HashMap<sys::MaaSinkId, usize>>,
28 event_sinks: Mutex<HashMap<sys::MaaSinkId, usize>>,
29}
30
31unsafe impl Send for ControllerInner {}
32unsafe impl Sync for ControllerInner {}
33
34unsafe impl Send for Controller {}
36unsafe impl Sync for Controller {}
37
38impl std::fmt::Debug for Controller {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("Controller")
41 .field("handle", &self.inner.handle)
42 .finish()
43 }
44}
45
46impl Controller {
47 #[cfg(feature = "adb")]
55 pub fn new_adb(
56 adb_path: &str,
57 address: &str,
58 config: &str,
59 agent_path: &str,
60 ) -> MaaResult<Self> {
61 Self::create_adb(
62 adb_path,
63 address,
64 sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
65 sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
66 config,
67 agent_path,
68 )
69 }
70
71 #[cfg(feature = "adb")]
73 fn resolve_agent_path(agent_path: &str) -> MaaResult<String> {
74 if !agent_path.is_empty() {
75 return Ok(agent_path.to_string());
76 }
77 let cur = std::env::current_dir().map_err(|e| {
78 MaaError::InvalidArgument(format!("agent_path empty and current_dir failed: {}", e))
79 })?;
80 let s = cur.to_str().ok_or_else(|| {
81 MaaError::InvalidArgument(
82 "agent_path empty and current directory is not valid UTF-8".to_string(),
83 )
84 })?;
85 Ok(s.to_string())
86 }
87
88 #[cfg(feature = "adb")]
89 pub(crate) fn create_adb(
90 adb_path: &str,
91 address: &str,
92 screencap_method: sys::MaaAdbScreencapMethod,
93 input_method: sys::MaaAdbInputMethod,
94 config: &str,
95 agent_path: &str,
96 ) -> MaaResult<Self> {
97 let path = Self::resolve_agent_path(agent_path)?;
98 let c_adb = CString::new(adb_path)?;
99 let c_addr = CString::new(address)?;
100 let c_cfg = CString::new(config)?;
101 let c_agent = CString::new(path.as_str())?;
102
103 let handle = unsafe {
104 sys::MaaAdbControllerCreate(
105 c_adb.as_ptr(),
106 c_addr.as_ptr(),
107 screencap_method,
108 input_method,
109 c_cfg.as_ptr(),
110 c_agent.as_ptr(),
111 )
112 };
113
114 if let Some(ptr) = NonNull::new(handle) {
115 Ok(Self {
116 inner: Arc::new(ControllerInner {
117 handle: ptr,
118 owns_handle: true,
119 callbacks: Mutex::new(HashMap::new()),
120 event_sinks: Mutex::new(HashMap::new()),
121 }),
122 })
123 } else {
124 Err(MaaError::FrameworkError(-1))
125 }
126 }
127
128 #[cfg(feature = "win32")]
130 pub fn new_win32(
131 hwnd: *mut c_void,
132 screencap_method: sys::MaaWin32ScreencapMethod,
133 mouse_method: sys::MaaWin32InputMethod,
134 keyboard_method: sys::MaaWin32InputMethod,
135 ) -> MaaResult<Self> {
136 let handle = unsafe {
137 sys::MaaWin32ControllerCreate(hwnd, screencap_method, mouse_method, keyboard_method)
138 };
139
140 Self::from_handle(handle)
141 }
142
143 pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self> {
146 let c_addr = CString::new(address)?;
147 let c_uuid = CString::new(uuid)?;
148 let handle = unsafe { sys::MaaPlayCoverControllerCreate(c_addr.as_ptr(), c_uuid.as_ptr()) };
149
150 Self::from_handle(handle)
151 }
152
153 pub fn new_wlroots(wlr_socket_path: &str) -> MaaResult<Self> {
158 let c_path = CString::new(wlr_socket_path)?;
159 let handle = unsafe { sys::MaaWlRootsControllerCreate(c_path.as_ptr()) };
160
161 Self::from_handle(handle)
162 }
163
164 #[cfg(feature = "custom")]
166 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
167 callback: T,
168 ) -> MaaResult<Self> {
169 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
170 Box::new(Box::new(callback));
171 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
172 let callbacks = crate::custom_controller::get_callbacks();
173 let handle =
174 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
175
176 NonNull::new(handle)
177 .map(|ptr| Self {
178 inner: Arc::new(ControllerInner {
179 handle: ptr,
180 owns_handle: true,
181 callbacks: Mutex::new(HashMap::new()),
182 event_sinks: Mutex::new(HashMap::new()),
183 }),
184 })
185 .ok_or_else(|| {
186 unsafe {
187 let _ = Box::from_raw(
188 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
189 );
190 }
191 MaaError::FrameworkError(-1)
192 })
193 }
194
195 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
197 if let Some(ptr) = NonNull::new(handle) {
198 Ok(Self {
199 inner: Arc::new(ControllerInner {
200 handle: ptr,
201 owns_handle: true,
202 callbacks: Mutex::new(HashMap::new()),
203 event_sinks: Mutex::new(HashMap::new()),
204 }),
205 })
206 } else {
207 Err(MaaError::FrameworkError(-1))
208 }
209 }
210
211 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
213 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
214 Ok(id)
215 }
216
217 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
219 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
220 Ok(id)
221 }
222
223 pub fn post_click_v2(
230 &self,
231 x: i32,
232 y: i32,
233 contact: i32,
234 pressure: i32,
235 ) -> MaaResult<common::MaaId> {
236 let id = unsafe {
237 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
238 };
239 Ok(id)
240 }
241
242 pub fn post_swipe(
249 &self,
250 x1: i32,
251 y1: i32,
252 x2: i32,
253 y2: i32,
254 duration: i32,
255 ) -> MaaResult<common::MaaId> {
256 let id = unsafe {
257 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
258 };
259 Ok(id)
260 }
261
262 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
267 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
268 Ok(id)
269 }
270
271 #[deprecated(note = "Use post_click_key instead")]
273 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
274 self.post_click_key(keycode)
275 }
276
277 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
282 let c_text = CString::new(text)?;
283 let id =
284 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
285 Ok(id)
286 }
287
288 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
294 let c_cmd = CString::new(cmd)?;
295 let id = unsafe {
296 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
297 };
298 Ok(id)
299 }
300
301 pub fn post_touch_down(
308 &self,
309 contact: i32,
310 x: i32,
311 y: i32,
312 pressure: i32,
313 ) -> MaaResult<common::MaaId> {
314 let id = unsafe {
315 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
316 };
317 Ok(id)
318 }
319
320 pub fn post_touch_move(
327 &self,
328 contact: i32,
329 x: i32,
330 y: i32,
331 pressure: i32,
332 ) -> MaaResult<common::MaaId> {
333 let id = unsafe {
334 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
335 };
336 Ok(id)
337 }
338
339 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
344 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
345 Ok(id)
346 }
347
348 #[inline]
350 pub fn raw(&self) -> *mut sys::MaaController {
351 self.inner.handle.as_ptr()
352 }
353
354 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
360 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
361 Ok(id)
362 }
363
364 pub fn connected(&self) -> bool {
366 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
367 }
368
369 pub fn uuid(&self) -> MaaResult<String> {
371 let buffer = crate::buffer::MaaStringBuffer::new()?;
372 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
373 if ret != 0 {
374 Ok(buffer.to_string())
375 } else {
376 Err(MaaError::FrameworkError(0))
377 }
378 }
379
380 pub fn info(&self) -> MaaResult<serde_json::Value> {
385 let buffer = crate::buffer::MaaStringBuffer::new()?;
386 let ret = unsafe { sys::MaaControllerGetInfo(self.inner.handle.as_ptr(), buffer.as_ptr()) };
387 if ret != 0 {
388 serde_json::from_str(&buffer.to_string()).map_err(|e| {
389 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
390 })
391 } else {
392 Err(MaaError::FrameworkError(0))
393 }
394 }
395
396 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
398 let mut width: i32 = 0;
399 let mut height: i32 = 0;
400 let ret = unsafe {
401 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
402 };
403 if ret != 0 {
404 Ok((width, height))
405 } else {
406 Err(MaaError::FrameworkError(0))
407 }
408 }
409
410 pub fn post_swipe_v2(
421 &self,
422 x1: i32,
423 y1: i32,
424 x2: i32,
425 y2: i32,
426 duration: i32,
427 contact: i32,
428 pressure: i32,
429 ) -> MaaResult<common::MaaId> {
430 let id = unsafe {
431 sys::MaaControllerPostSwipeV2(
432 self.inner.handle.as_ptr(),
433 x1,
434 y1,
435 x2,
436 y2,
437 duration,
438 contact,
439 pressure,
440 )
441 };
442 Ok(id)
443 }
444
445 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
449 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
450 Ok(id)
451 }
452
453 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
455 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
456 Ok(id)
457 }
458
459 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
466 let c_intent = CString::new(intent)?;
467 let id = unsafe {
468 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
469 };
470 Ok(id)
471 }
472
473 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
478 let c_intent = CString::new(intent)?;
479 let id =
480 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
481 Ok(id)
482 }
483
484 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
492 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
493 Ok(id)
494 }
495
496 pub fn post_inactive(&self) -> MaaResult<common::MaaId> {
503 let id = unsafe { sys::MaaControllerPostInactive(self.inner.handle.as_ptr()) };
504 Ok(id)
505 }
506
507 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
511 let buffer = crate::buffer::MaaImageBuffer::new()?;
512 let ret =
513 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
514 if ret != 0 {
515 Ok(buffer)
516 } else {
517 Err(MaaError::FrameworkError(0))
518 }
519 }
520
521 pub fn shell_output(&self) -> MaaResult<String> {
525 let buffer = crate::buffer::MaaStringBuffer::new()?;
526 let ret = unsafe {
527 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
528 };
529 if ret != 0 {
530 Ok(buffer.to_string())
531 } else {
532 Err(MaaError::FrameworkError(0))
533 }
534 }
535
536 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
540 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
541 common::MaaStatus(s)
542 }
543
544 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
546 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
547 common::MaaStatus(s)
548 }
549
550 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
554 let mut val = long_side;
555 let ret = unsafe {
556 sys::MaaControllerSetOption(
557 self.inner.handle.as_ptr(),
558 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
559 &mut val as *mut _ as *mut c_void,
560 std::mem::size_of::<i32>() as u64,
561 )
562 };
563 common::check_bool(ret)
564 }
565
566 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
568 let mut val = short_side;
569 let ret = unsafe {
570 sys::MaaControllerSetOption(
571 self.inner.handle.as_ptr(),
572 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
573 &mut val as *mut _ as *mut c_void,
574 std::mem::size_of::<i32>() as u64,
575 )
576 };
577 common::check_bool(ret)
578 }
579
580 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
582 let mut val: u8 = if enable { 1 } else { 0 };
583 let ret = unsafe {
584 sys::MaaControllerSetOption(
585 self.inner.handle.as_ptr(),
586 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
587 &mut val as *mut _ as *mut c_void,
588 std::mem::size_of::<u8>() as u64,
589 )
590 };
591 common::check_bool(ret)
592 }
593
594 #[cfg(feature = "dbg")]
597 pub fn new_dbg(
598 read_path: &str,
599 write_path: &str,
600 dbg_type: sys::MaaDbgControllerType,
601 config: &str,
602 ) -> MaaResult<Self> {
603 let c_read = CString::new(read_path)?;
604 let c_write = CString::new(write_path)?;
605 let c_cfg = CString::new(config)?;
606 let handle = unsafe {
607 sys::MaaDbgControllerCreate(c_read.as_ptr(), c_write.as_ptr(), dbg_type, c_cfg.as_ptr())
608 };
609 Self::from_handle(handle)
610 }
611
612 #[cfg(feature = "win32")]
614 pub fn new_gamepad(
615 hwnd: *mut c_void,
616 gamepad_type: crate::common::GamepadType,
617 screencap_method: crate::common::Win32ScreencapMethod,
618 ) -> MaaResult<Self> {
619 let handle = unsafe {
620 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
621 };
622 Self::from_handle(handle)
623 }
624
625 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
629 where
630 F: Fn(&str, &str) + Send + Sync + 'static,
631 {
632 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
633 let sink_id =
634 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
635 if sink_id != 0 {
636 self.inner
637 .callbacks
638 .lock()
639 .unwrap()
640 .insert(sink_id, cb_arg as usize);
641 Ok(sink_id)
642 } else {
643 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
644 Err(MaaError::FrameworkError(0))
645 }
646 }
647
648 pub fn add_event_sink(
660 &self,
661 sink: Box<dyn crate::event_sink::EventSink>,
662 ) -> MaaResult<sys::MaaSinkId> {
663 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
664 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
665 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
666 if id > 0 {
667 self.inner
668 .event_sinks
669 .lock()
670 .unwrap()
671 .insert(id, arg as usize);
672 Ok(id)
673 } else {
674 unsafe { crate::callback::EventCallback::drop_sink(arg) };
675 Err(MaaError::FrameworkError(0))
676 }
677 }
678
679 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
680 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
681 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
682 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
683 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
684 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
685 }
686 }
687
688 pub fn clear_sinks(&self) {
689 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
690 let mut callbacks = self.inner.callbacks.lock().unwrap();
691 for (_, ptr) in callbacks.drain() {
692 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
693 }
694 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
695 for (_, ptr) in event_sinks.drain() {
696 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
697 }
698 }
699}
700
701impl Drop for ControllerInner {
702 fn drop(&mut self) {
703 unsafe {
704 sys::MaaControllerClearSinks(self.handle.as_ptr());
705 let mut callbacks = self.callbacks.lock().unwrap();
706 for (_, ptr) in callbacks.drain() {
707 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
708 }
709 let mut event_sinks = self.event_sinks.lock().unwrap();
710 for (_, ptr) in event_sinks.drain() {
711 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
712 }
713 if self.owns_handle {
714 sys::MaaControllerDestroy(self.handle.as_ptr());
715 }
716 }
717 }
718}
719
720#[cfg(feature = "adb")]
724pub struct AdbControllerBuilder {
725 adb_path: String,
726 address: String,
727 screencap_methods: sys::MaaAdbScreencapMethod,
728 input_methods: sys::MaaAdbInputMethod,
729 config: String,
730 agent_path: String,
731}
732
733#[cfg(feature = "adb")]
734impl AdbControllerBuilder {
735 pub fn new(adb_path: &str, address: &str) -> Self {
737 Self {
738 adb_path: adb_path.to_string(),
739 address: address.to_string(),
740 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
741 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
742 config: "{}".to_string(),
743 agent_path: String::new(),
744 }
745 }
746
747 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
749 self.screencap_methods = methods;
750 self
751 }
752
753 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
755 self.input_methods = methods;
756 self
757 }
758
759 pub fn config(mut self, config: &str) -> Self {
761 self.config = config.to_string();
762 self
763 }
764
765 pub fn agent_path(mut self, path: &str) -> Self {
767 self.agent_path = path.to_string();
768 self
769 }
770
771 pub fn build(self) -> MaaResult<Controller> {
773 Controller::create_adb(
774 &self.adb_path,
775 &self.address,
776 self.screencap_methods,
777 self.input_methods,
778 &self.config,
779 &self.agent_path,
780 )
781 }
782}
783
784pub struct ControllerRef<'a> {
790 handle: *mut sys::MaaController,
791 _marker: std::marker::PhantomData<&'a ()>,
792}
793
794impl<'a> std::fmt::Debug for ControllerRef<'a> {
795 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
796 f.debug_struct("ControllerRef")
797 .field("handle", &self.handle)
798 .finish()
799 }
800}
801
802impl<'a> ControllerRef<'a> {
803 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
804 if handle.is_null() {
805 None
806 } else {
807 Some(Self {
808 handle,
809 _marker: std::marker::PhantomData,
810 })
811 }
812 }
813
814 pub fn connected(&self) -> bool {
816 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
817 }
818
819 pub fn uuid(&self) -> MaaResult<String> {
821 let buffer = crate::buffer::MaaStringBuffer::new()?;
822 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
823 if ret != 0 {
824 Ok(buffer.to_string())
825 } else {
826 Err(MaaError::FrameworkError(0))
827 }
828 }
829
830 pub fn info(&self) -> MaaResult<serde_json::Value> {
832 let buffer = crate::buffer::MaaStringBuffer::new()?;
833 let ret = unsafe { sys::MaaControllerGetInfo(self.handle, buffer.as_ptr()) };
834 if ret != 0 {
835 serde_json::from_str(&buffer.to_string()).map_err(|e| {
836 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
837 })
838 } else {
839 Err(MaaError::FrameworkError(0))
840 }
841 }
842
843 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
845 let mut width: i32 = 0;
846 let mut height: i32 = 0;
847 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
848 if ret != 0 {
849 Ok((width, height))
850 } else {
851 Err(MaaError::FrameworkError(0))
852 }
853 }
854
855 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
857 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
858 common::MaaStatus(s)
859 }
860
861 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
863 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
864 common::MaaStatus(s)
865 }
866
867 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
869 let buffer = crate::buffer::MaaImageBuffer::new()?;
870 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
871 if ret != 0 {
872 Ok(buffer)
873 } else {
874 Err(MaaError::FrameworkError(0))
875 }
876 }
877
878 pub fn raw(&self) -> *mut sys::MaaController {
880 self.handle
881 }
882}