1use crate::{MaaError, MaaResult, common, sys};
4use serde::Serialize;
5use std::collections::HashMap;
6use std::ffi::CString;
7use std::os::raw::c_void;
8#[cfg(feature = "dynamic")]
9use std::panic::AssertUnwindSafe;
10use std::ptr::NonNull;
11use std::sync::{Arc, Mutex};
12
13#[derive(Clone)]
23pub struct Controller {
24 inner: Arc<ControllerInner>,
25}
26
27struct ControllerInner {
28 handle: NonNull<sys::MaaController>,
29 owns_handle: bool,
30 _retained_handles: Vec<Arc<ControllerInner>>,
31 callbacks: Mutex<HashMap<sys::MaaSinkId, usize>>,
32 event_sinks: Mutex<HashMap<sys::MaaSinkId, usize>>,
33}
34
35unsafe impl Send for ControllerInner {}
36unsafe impl Sync for ControllerInner {}
37
38unsafe impl Send for Controller {}
40unsafe impl Sync for Controller {}
41
42impl std::fmt::Debug for Controller {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("Controller")
45 .field("handle", &self.inner.handle)
46 .finish()
47 }
48}
49
50impl Controller {
51 #[cfg(feature = "adb")]
59 pub fn new_adb(
60 adb_path: &str,
61 address: &str,
62 config: &str,
63 agent_path: &str,
64 ) -> MaaResult<Self> {
65 Self::create_adb(
66 adb_path,
67 address,
68 sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
69 sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
70 config,
71 agent_path,
72 )
73 }
74
75 #[cfg(feature = "adb")]
77 fn resolve_agent_path(agent_path: &str) -> MaaResult<String> {
78 if !agent_path.is_empty() {
79 return Ok(agent_path.to_string());
80 }
81 let cur = std::env::current_dir().map_err(|e| {
82 MaaError::InvalidArgument(format!("agent_path empty and current_dir failed: {}", e))
83 })?;
84 let s = cur.to_str().ok_or_else(|| {
85 MaaError::InvalidArgument(
86 "agent_path empty and current directory is not valid UTF-8".to_string(),
87 )
88 })?;
89 Ok(s.to_string())
90 }
91
92 #[cfg(feature = "adb")]
93 pub(crate) fn create_adb(
94 adb_path: &str,
95 address: &str,
96 screencap_method: sys::MaaAdbScreencapMethod,
97 input_method: sys::MaaAdbInputMethod,
98 config: &str,
99 agent_path: &str,
100 ) -> MaaResult<Self> {
101 let path = Self::resolve_agent_path(agent_path)?;
102 let c_adb = CString::new(adb_path)?;
103 let c_addr = CString::new(address)?;
104 let c_cfg = CString::new(config)?;
105 let c_agent = CString::new(path.as_str())?;
106
107 let handle = unsafe {
108 sys::MaaAdbControllerCreate(
109 c_adb.as_ptr(),
110 c_addr.as_ptr(),
111 screencap_method,
112 input_method,
113 c_cfg.as_ptr(),
114 c_agent.as_ptr(),
115 )
116 };
117
118 if let Some(ptr) = NonNull::new(handle) {
119 Ok(Self::new_owned(ptr))
120 } else {
121 Err(MaaError::FrameworkError(-1))
122 }
123 }
124
125 #[cfg(feature = "win32")]
127 pub fn new_win32(
128 hwnd: *mut c_void,
129 screencap_method: sys::MaaWin32ScreencapMethod,
130 mouse_method: sys::MaaWin32InputMethod,
131 keyboard_method: sys::MaaWin32InputMethod,
132 ) -> MaaResult<Self> {
133 let handle = unsafe {
134 sys::MaaWin32ControllerCreate(hwnd, screencap_method, mouse_method, keyboard_method)
135 };
136
137 Self::from_handle(handle)
138 }
139
140 pub fn new_macos(
147 window_id: u32,
148 screencap_method: sys::MaaMacOSScreencapMethod,
149 input_method: sys::MaaMacOSInputMethod,
150 ) -> MaaResult<Self> {
151 let handle =
152 unsafe { sys::MaaMacOSControllerCreate(window_id, screencap_method, input_method) };
153
154 Self::from_handle(handle)
155 }
156
157 pub fn new_android_native<T: Serialize>(config: &T) -> MaaResult<Self> {
164 let config_json = serde_json::to_string(config).map_err(|e| {
165 MaaError::InvalidConfig(format!(
166 "Failed to serialize Android native controller config: {}",
167 e
168 ))
169 })?;
170 let c_config = CString::new(config_json)?;
171
172 #[cfg(feature = "dynamic")]
173 let handle = std::panic::catch_unwind(AssertUnwindSafe(|| unsafe {
174 sys::MaaAndroidNativeControllerCreate(c_config.as_ptr())
175 }))
176 .map_err(|_| {
177 MaaError::InvalidArgument(
178 "Android native controller is not available in this MaaFramework build".to_string(),
179 )
180 })?;
181
182 #[cfg(not(feature = "dynamic"))]
183 let handle = unsafe { sys::MaaAndroidNativeControllerCreate(c_config.as_ptr()) };
184
185 Self::from_handle(handle)
186 }
187
188 pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self> {
191 let c_addr = CString::new(address)?;
192 let c_uuid = CString::new(uuid)?;
193 let handle = unsafe { sys::MaaPlayCoverControllerCreate(c_addr.as_ptr(), c_uuid.as_ptr()) };
194
195 Self::from_handle(handle)
196 }
197
198 pub fn new_wlroots(wlr_socket_path: &str) -> MaaResult<Self> {
203 Self::new_wlroots_with_vk_code(wlr_socket_path, false)
204 }
205
206 pub fn new_wlroots_with_vk_code(
213 wlr_socket_path: &str,
214 use_win32_vk_code: bool,
215 ) -> MaaResult<Self> {
216 let c_path = CString::new(wlr_socket_path)?;
217 let handle = unsafe {
218 sys::MaaWlRootsControllerCreate(c_path.as_ptr(), use_win32_vk_code as sys::MaaBool)
219 };
220
221 Self::from_handle(handle)
222 }
223
224 pub fn new_kwin(device_node: &str, screen_width: i32, screen_height: i32) -> MaaResult<Self> {
240 let c_node = CString::new(device_node)?;
241 let handle =
242 unsafe { sys::MaaKWinControllerCreate(c_node.as_ptr(), screen_width, screen_height) };
243
244 Self::from_handle(handle)
245 }
246
247 #[cfg(feature = "custom")]
249 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
250 callback: T,
251 ) -> MaaResult<Self> {
252 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
253 Box::new(Box::new(callback));
254 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
255 let callbacks = crate::custom_controller::get_callbacks();
256 let handle =
257 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
258
259 NonNull::new(handle).map(Self::new_owned).ok_or_else(|| {
260 unsafe {
261 let _ = Box::from_raw(
262 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
263 );
264 }
265 MaaError::FrameworkError(-1)
266 })
267 }
268
269 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
271 if let Some(ptr) = NonNull::new(handle) {
272 Ok(Self::new_owned(ptr))
273 } else {
274 Err(MaaError::FrameworkError(-1))
275 }
276 }
277
278 fn new_owned(handle: NonNull<sys::MaaController>) -> Self {
279 Self::new_with_retained(handle, Vec::new())
280 }
281
282 fn new_with_retained(
283 handle: NonNull<sys::MaaController>,
284 retained_handles: Vec<Arc<ControllerInner>>,
285 ) -> Self {
286 Self {
287 inner: Arc::new(ControllerInner {
288 handle,
289 owns_handle: true,
290 _retained_handles: retained_handles,
291 callbacks: Mutex::new(HashMap::new()),
292 event_sinks: Mutex::new(HashMap::new()),
293 }),
294 }
295 }
296
297 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
299 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
300 Ok(id)
301 }
302
303 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
305 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
306 Ok(id)
307 }
308
309 pub fn post_click_v2(
316 &self,
317 x: i32,
318 y: i32,
319 contact: i32,
320 pressure: i32,
321 ) -> MaaResult<common::MaaId> {
322 let id = unsafe {
323 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
324 };
325 Ok(id)
326 }
327
328 pub fn post_swipe(
335 &self,
336 x1: i32,
337 y1: i32,
338 x2: i32,
339 y2: i32,
340 duration: i32,
341 ) -> MaaResult<common::MaaId> {
342 let id = unsafe {
343 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
344 };
345 Ok(id)
346 }
347
348 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
353 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
354 Ok(id)
355 }
356
357 #[deprecated(note = "Use post_click_key instead")]
359 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
360 self.post_click_key(keycode)
361 }
362
363 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
368 let c_text = CString::new(text)?;
369 let id =
370 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
371 Ok(id)
372 }
373
374 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
380 let c_cmd = CString::new(cmd)?;
381 let id = unsafe {
382 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
383 };
384 Ok(id)
385 }
386
387 pub fn post_touch_down(
394 &self,
395 contact: i32,
396 x: i32,
397 y: i32,
398 pressure: i32,
399 ) -> MaaResult<common::MaaId> {
400 let id = unsafe {
401 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
402 };
403 Ok(id)
404 }
405
406 pub fn post_touch_move(
413 &self,
414 contact: i32,
415 x: i32,
416 y: i32,
417 pressure: i32,
418 ) -> MaaResult<common::MaaId> {
419 let id = unsafe {
420 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
421 };
422 Ok(id)
423 }
424
425 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
430 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
431 Ok(id)
432 }
433
434 pub fn post_relative_move(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
440 let id = unsafe { sys::MaaControllerPostRelativeMove(self.inner.handle.as_ptr(), dx, dy) };
441 Ok(id)
442 }
443
444 #[inline]
446 pub fn raw(&self) -> *mut sys::MaaController {
447 self.inner.handle.as_ptr()
448 }
449
450 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
456 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
457 Ok(id)
458 }
459
460 pub fn connected(&self) -> bool {
462 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
463 }
464
465 pub fn uuid(&self) -> MaaResult<String> {
467 let buffer = crate::buffer::MaaStringBuffer::new()?;
468 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
469 if ret != 0 {
470 Ok(buffer.to_string())
471 } else {
472 Err(MaaError::FrameworkError(0))
473 }
474 }
475
476 pub fn info(&self) -> MaaResult<serde_json::Value> {
481 let buffer = crate::buffer::MaaStringBuffer::new()?;
482 let ret = unsafe { sys::MaaControllerGetInfo(self.inner.handle.as_ptr(), buffer.as_ptr()) };
483 if ret != 0 {
484 serde_json::from_str(&buffer.to_string()).map_err(|e| {
485 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
486 })
487 } else {
488 Err(MaaError::FrameworkError(0))
489 }
490 }
491
492 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
494 let mut width: i32 = 0;
495 let mut height: i32 = 0;
496 let ret = unsafe {
497 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
498 };
499 if ret != 0 {
500 Ok((width, height))
501 } else {
502 Err(MaaError::FrameworkError(0))
503 }
504 }
505
506 pub fn post_swipe_v2(
517 &self,
518 x1: i32,
519 y1: i32,
520 x2: i32,
521 y2: i32,
522 duration: i32,
523 contact: i32,
524 pressure: i32,
525 ) -> MaaResult<common::MaaId> {
526 let id = unsafe {
527 sys::MaaControllerPostSwipeV2(
528 self.inner.handle.as_ptr(),
529 x1,
530 y1,
531 x2,
532 y2,
533 duration,
534 contact,
535 pressure,
536 )
537 };
538 Ok(id)
539 }
540
541 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
545 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
546 Ok(id)
547 }
548
549 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
551 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
552 Ok(id)
553 }
554
555 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
562 let c_intent = CString::new(intent)?;
563 let id = unsafe {
564 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
565 };
566 Ok(id)
567 }
568
569 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
574 let c_intent = CString::new(intent)?;
575 let id =
576 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
577 Ok(id)
578 }
579
580 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
588 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
589 Ok(id)
590 }
591
592 pub fn post_inactive(&self) -> MaaResult<common::MaaId> {
599 let id = unsafe { sys::MaaControllerPostInactive(self.inner.handle.as_ptr()) };
600 Ok(id)
601 }
602
603 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
607 let buffer = crate::buffer::MaaImageBuffer::new()?;
608 let ret =
609 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
610 if ret != 0 {
611 Ok(buffer)
612 } else {
613 Err(MaaError::FrameworkError(0))
614 }
615 }
616
617 pub fn shell_output(&self) -> MaaResult<String> {
621 let buffer = crate::buffer::MaaStringBuffer::new()?;
622 let ret = unsafe {
623 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
624 };
625 if ret != 0 {
626 Ok(buffer.to_string())
627 } else {
628 Err(MaaError::FrameworkError(0))
629 }
630 }
631
632 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
636 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
637 common::MaaStatus(s)
638 }
639
640 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
642 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
643 common::MaaStatus(s)
644 }
645
646 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
650 let mut val = long_side;
651 let ret = unsafe {
652 sys::MaaControllerSetOption(
653 self.inner.handle.as_ptr(),
654 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
655 &mut val as *mut _ as *mut c_void,
656 std::mem::size_of::<i32>() as u64,
657 )
658 };
659 common::check_bool(ret)
660 }
661
662 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
664 let mut val = short_side;
665 let ret = unsafe {
666 sys::MaaControllerSetOption(
667 self.inner.handle.as_ptr(),
668 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
669 &mut val as *mut _ as *mut c_void,
670 std::mem::size_of::<i32>() as u64,
671 )
672 };
673 common::check_bool(ret)
674 }
675
676 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
678 let mut val: u8 = if enable { 1 } else { 0 };
679 let ret = unsafe {
680 sys::MaaControllerSetOption(
681 self.inner.handle.as_ptr(),
682 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
683 &mut val as *mut _ as *mut c_void,
684 std::mem::size_of::<u8>() as u64,
685 )
686 };
687 common::check_bool(ret)
688 }
689
690 pub fn set_screenshot_resize_method(&self, method: i32) -> MaaResult<()> {
699 let mut val = method;
700 let ret = unsafe {
701 sys::MaaControllerSetOption(
702 self.inner.handle.as_ptr(),
703 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotResizeMethod as i32,
704 &mut val as *mut _ as *mut c_void,
705 std::mem::size_of::<i32>() as u64,
706 )
707 };
708 common::check_bool(ret)
709 }
710
711 pub fn set_mouse_lock_follow(&self, enable: bool) -> MaaResult<()> {
716 let mut val: u8 = if enable { 1 } else { 0 };
717 let ret = unsafe {
718 sys::MaaControllerSetOption(
719 self.inner.handle.as_ptr(),
720 sys::MaaCtrlOptionEnum_MaaCtrlOption_MouseLockFollow as i32,
721 &mut val as *mut _ as *mut c_void,
722 std::mem::size_of::<u8>() as u64,
723 )
724 };
725 common::check_bool(ret)
726 }
727
728 pub fn set_background_managed_keys(&self, keys: &[i32]) -> MaaResult<()> {
736 let ret = unsafe {
737 sys::MaaControllerSetOption(
738 self.inner.handle.as_ptr(),
739 sys::MaaCtrlOptionEnum_MaaCtrlOption_BackgroundManagedKeys as i32,
740 keys.as_ptr() as *mut c_void,
741 std::mem::size_of_val(keys) as u64,
742 )
743 };
744 common::check_bool(ret)
745 }
746
747 pub fn new_dbg(read_path: &str) -> MaaResult<Self> {
750 let c_read = CString::new(read_path)?;
751 let handle = unsafe { sys::MaaDbgControllerCreate(c_read.as_ptr()) };
752 Self::from_handle(handle)
753 }
754
755 pub fn new_replay(recording_path: &str) -> MaaResult<Self> {
757 let c_recording = CString::new(recording_path)?;
758 let handle = unsafe { sys::MaaReplayControllerCreate(c_recording.as_ptr()) };
759 Self::from_handle(handle)
760 }
761
762 pub fn new_record(inner: &Controller, recording_path: &str) -> MaaResult<Self> {
764 let c_recording = CString::new(recording_path)?;
765 let handle = unsafe { sys::MaaRecordControllerCreate(inner.raw(), c_recording.as_ptr()) };
766
767 if let Some(ptr) = NonNull::new(handle) {
768 Ok(Self::new_with_retained(ptr, vec![Arc::clone(&inner.inner)]))
769 } else {
770 Err(MaaError::FrameworkError(-1))
771 }
772 }
773
774 #[cfg(feature = "win32")]
776 pub fn new_gamepad(
777 hwnd: *mut c_void,
778 gamepad_type: crate::common::GamepadType,
779 screencap_method: crate::common::Win32ScreencapMethod,
780 ) -> MaaResult<Self> {
781 let handle = unsafe {
782 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
783 };
784 Self::from_handle(handle)
785 }
786
787 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
791 where
792 F: Fn(&str, &str) + Send + Sync + 'static,
793 {
794 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
795 let sink_id =
796 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
797 if sink_id != 0 {
798 self.inner
799 .callbacks
800 .lock()
801 .unwrap()
802 .insert(sink_id, cb_arg as usize);
803 Ok(sink_id)
804 } else {
805 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
806 Err(MaaError::FrameworkError(0))
807 }
808 }
809
810 pub fn add_event_sink(
822 &self,
823 sink: Box<dyn crate::event_sink::EventSink>,
824 ) -> MaaResult<sys::MaaSinkId> {
825 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
826 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
827 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
828 if id > 0 {
829 self.inner
830 .event_sinks
831 .lock()
832 .unwrap()
833 .insert(id, arg as usize);
834 Ok(id)
835 } else {
836 unsafe { crate::callback::EventCallback::drop_sink(arg) };
837 Err(MaaError::FrameworkError(0))
838 }
839 }
840
841 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
842 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
843 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
844 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
845 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
846 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
847 }
848 }
849
850 pub fn clear_sinks(&self) {
851 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
852 let mut callbacks = self.inner.callbacks.lock().unwrap();
853 for (_, ptr) in callbacks.drain() {
854 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
855 }
856 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
857 for (_, ptr) in event_sinks.drain() {
858 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
859 }
860 }
861}
862
863impl Drop for ControllerInner {
864 fn drop(&mut self) {
865 unsafe {
866 if self.owns_handle {
867 sys::MaaControllerClearSinks(self.handle.as_ptr());
868 let mut callbacks = self.callbacks.lock().unwrap();
869 for (_, ptr) in callbacks.drain() {
870 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
871 }
872 let mut event_sinks = self.event_sinks.lock().unwrap();
873 for (_, ptr) in event_sinks.drain() {
874 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
875 }
876 sys::MaaControllerDestroy(self.handle.as_ptr());
877 }
878 }
879 }
880}
881
882#[cfg(feature = "adb")]
886pub struct AdbControllerBuilder {
887 adb_path: String,
888 address: String,
889 screencap_methods: sys::MaaAdbScreencapMethod,
890 input_methods: sys::MaaAdbInputMethod,
891 config: String,
892 agent_path: String,
893}
894
895#[cfg(feature = "adb")]
896impl AdbControllerBuilder {
897 pub fn new(adb_path: &str, address: &str) -> Self {
899 Self {
900 adb_path: adb_path.to_string(),
901 address: address.to_string(),
902 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
903 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
904 config: "{}".to_string(),
905 agent_path: String::new(),
906 }
907 }
908
909 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
911 self.screencap_methods = methods;
912 self
913 }
914
915 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
917 self.input_methods = methods;
918 self
919 }
920
921 pub fn config(mut self, config: &str) -> Self {
923 self.config = config.to_string();
924 self
925 }
926
927 pub fn agent_path(mut self, path: &str) -> Self {
929 self.agent_path = path.to_string();
930 self
931 }
932
933 pub fn build(self) -> MaaResult<Controller> {
935 Controller::create_adb(
936 &self.adb_path,
937 &self.address,
938 self.screencap_methods,
939 self.input_methods,
940 &self.config,
941 &self.agent_path,
942 )
943 }
944}
945
946pub struct ControllerRef<'a> {
952 handle: *mut sys::MaaController,
953 _marker: std::marker::PhantomData<&'a ()>,
954}
955
956impl<'a> std::fmt::Debug for ControllerRef<'a> {
957 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
958 f.debug_struct("ControllerRef")
959 .field("handle", &self.handle)
960 .finish()
961 }
962}
963
964impl<'a> ControllerRef<'a> {
965 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
966 if handle.is_null() {
967 None
968 } else {
969 Some(Self {
970 handle,
971 _marker: std::marker::PhantomData,
972 })
973 }
974 }
975
976 pub fn connected(&self) -> bool {
978 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
979 }
980
981 pub fn uuid(&self) -> MaaResult<String> {
983 let buffer = crate::buffer::MaaStringBuffer::new()?;
984 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
985 if ret != 0 {
986 Ok(buffer.to_string())
987 } else {
988 Err(MaaError::FrameworkError(0))
989 }
990 }
991
992 pub fn info(&self) -> MaaResult<serde_json::Value> {
994 let buffer = crate::buffer::MaaStringBuffer::new()?;
995 let ret = unsafe { sys::MaaControllerGetInfo(self.handle, buffer.as_ptr()) };
996 if ret != 0 {
997 serde_json::from_str(&buffer.to_string()).map_err(|e| {
998 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
999 })
1000 } else {
1001 Err(MaaError::FrameworkError(0))
1002 }
1003 }
1004
1005 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
1007 let mut width: i32 = 0;
1008 let mut height: i32 = 0;
1009 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
1010 if ret != 0 {
1011 Ok((width, height))
1012 } else {
1013 Err(MaaError::FrameworkError(0))
1014 }
1015 }
1016
1017 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1019 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
1020 common::MaaStatus(s)
1021 }
1022
1023 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1025 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
1026 common::MaaStatus(s)
1027 }
1028
1029 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
1031 let buffer = crate::buffer::MaaImageBuffer::new()?;
1032 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
1033 if ret != 0 {
1034 Ok(buffer)
1035 } else {
1036 Err(MaaError::FrameworkError(0))
1037 }
1038 }
1039
1040 pub fn raw(&self) -> *mut sys::MaaController {
1042 self.handle
1043 }
1044}