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 #[cfg(target_os = "linux")]
243 pub fn new_kwin(device_node: &str, screen_width: i32, screen_height: i32) -> MaaResult<Self> {
244 Self::new_kwin_with_vk_code(device_node, screen_width, screen_height, false)
245 }
246
247 #[cfg(target_os = "linux")]
259 pub fn new_kwin_with_vk_code(
260 device_node: &str,
261 screen_width: i32,
262 screen_height: i32,
263 use_win32_vk_code: bool,
264 ) -> MaaResult<Self> {
265 let c_node = CString::new(device_node)?;
266 let handle = unsafe {
267 sys::MaaKWinControllerCreate(
268 c_node.as_ptr(),
269 screen_width,
270 screen_height,
271 use_win32_vk_code as sys::MaaBool,
272 )
273 };
274
275 Self::from_handle(handle)
276 }
277
278 #[cfg(feature = "custom")]
280 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
281 callback: T,
282 ) -> MaaResult<Self> {
283 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
284 Box::new(Box::new(callback));
285 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
286 let callbacks = crate::custom_controller::get_callbacks();
287 let handle =
288 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
289
290 NonNull::new(handle).map(Self::new_owned).ok_or_else(|| {
291 unsafe {
292 let _ = Box::from_raw(
293 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
294 );
295 }
296 MaaError::FrameworkError(-1)
297 })
298 }
299
300 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
302 if let Some(ptr) = NonNull::new(handle) {
303 Ok(Self::new_owned(ptr))
304 } else {
305 Err(MaaError::FrameworkError(-1))
306 }
307 }
308
309 fn new_owned(handle: NonNull<sys::MaaController>) -> Self {
310 Self::new_with_retained(handle, Vec::new())
311 }
312
313 fn new_with_retained(
314 handle: NonNull<sys::MaaController>,
315 retained_handles: Vec<Arc<ControllerInner>>,
316 ) -> Self {
317 Self {
318 inner: Arc::new(ControllerInner {
319 handle,
320 owns_handle: true,
321 _retained_handles: retained_handles,
322 callbacks: Mutex::new(HashMap::new()),
323 event_sinks: Mutex::new(HashMap::new()),
324 }),
325 }
326 }
327
328 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
330 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
331 Ok(id)
332 }
333
334 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
336 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
337 Ok(id)
338 }
339
340 pub fn post_click_v2(
347 &self,
348 x: i32,
349 y: i32,
350 contact: i32,
351 pressure: i32,
352 ) -> MaaResult<common::MaaId> {
353 let id = unsafe {
354 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
355 };
356 Ok(id)
357 }
358
359 pub fn post_swipe(
366 &self,
367 x1: i32,
368 y1: i32,
369 x2: i32,
370 y2: i32,
371 duration: i32,
372 ) -> MaaResult<common::MaaId> {
373 let id = unsafe {
374 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
375 };
376 Ok(id)
377 }
378
379 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
384 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
385 Ok(id)
386 }
387
388 #[deprecated(note = "Use post_click_key instead")]
390 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
391 self.post_click_key(keycode)
392 }
393
394 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
399 let c_text = CString::new(text)?;
400 let id =
401 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
402 Ok(id)
403 }
404
405 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
411 let c_cmd = CString::new(cmd)?;
412 let id = unsafe {
413 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
414 };
415 Ok(id)
416 }
417
418 pub fn post_touch_down(
425 &self,
426 contact: i32,
427 x: i32,
428 y: i32,
429 pressure: i32,
430 ) -> MaaResult<common::MaaId> {
431 let id = unsafe {
432 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
433 };
434 Ok(id)
435 }
436
437 pub fn post_touch_move(
444 &self,
445 contact: i32,
446 x: i32,
447 y: i32,
448 pressure: i32,
449 ) -> MaaResult<common::MaaId> {
450 let id = unsafe {
451 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
452 };
453 Ok(id)
454 }
455
456 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
461 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
462 Ok(id)
463 }
464
465 pub fn post_relative_move(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
471 let id = unsafe { sys::MaaControllerPostRelativeMove(self.inner.handle.as_ptr(), dx, dy) };
472 Ok(id)
473 }
474
475 #[inline]
477 pub fn raw(&self) -> *mut sys::MaaController {
478 self.inner.handle.as_ptr()
479 }
480
481 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
487 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
488 Ok(id)
489 }
490
491 pub fn connected(&self) -> bool {
493 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
494 }
495
496 pub fn uuid(&self) -> MaaResult<String> {
498 let buffer = crate::buffer::MaaStringBuffer::new()?;
499 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
500 if ret != 0 {
501 Ok(buffer.to_string())
502 } else {
503 Err(MaaError::FrameworkError(0))
504 }
505 }
506
507 pub fn info(&self) -> MaaResult<serde_json::Value> {
512 let buffer = crate::buffer::MaaStringBuffer::new()?;
513 let ret = unsafe { sys::MaaControllerGetInfo(self.inner.handle.as_ptr(), buffer.as_ptr()) };
514 if ret != 0 {
515 serde_json::from_str(&buffer.to_string()).map_err(|e| {
516 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
517 })
518 } else {
519 Err(MaaError::FrameworkError(0))
520 }
521 }
522
523 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
525 let mut width: i32 = 0;
526 let mut height: i32 = 0;
527 let ret = unsafe {
528 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
529 };
530 if ret != 0 {
531 Ok((width, height))
532 } else {
533 Err(MaaError::FrameworkError(0))
534 }
535 }
536
537 pub fn post_swipe_v2(
548 &self,
549 x1: i32,
550 y1: i32,
551 x2: i32,
552 y2: i32,
553 duration: i32,
554 contact: i32,
555 pressure: i32,
556 ) -> MaaResult<common::MaaId> {
557 let id = unsafe {
558 sys::MaaControllerPostSwipeV2(
559 self.inner.handle.as_ptr(),
560 x1,
561 y1,
562 x2,
563 y2,
564 duration,
565 contact,
566 pressure,
567 )
568 };
569 Ok(id)
570 }
571
572 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
576 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
577 Ok(id)
578 }
579
580 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
582 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
583 Ok(id)
584 }
585
586 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
593 let c_intent = CString::new(intent)?;
594 let id = unsafe {
595 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
596 };
597 Ok(id)
598 }
599
600 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
605 let c_intent = CString::new(intent)?;
606 let id =
607 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
608 Ok(id)
609 }
610
611 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
619 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
620 Ok(id)
621 }
622
623 pub fn post_inactive(&self) -> MaaResult<common::MaaId> {
630 let id = unsafe { sys::MaaControllerPostInactive(self.inner.handle.as_ptr()) };
631 Ok(id)
632 }
633
634 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
638 let buffer = crate::buffer::MaaImageBuffer::new()?;
639 let ret =
640 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
641 if ret != 0 {
642 Ok(buffer)
643 } else {
644 Err(MaaError::FrameworkError(0))
645 }
646 }
647
648 pub fn shell_output(&self) -> MaaResult<String> {
652 let buffer = crate::buffer::MaaStringBuffer::new()?;
653 let ret = unsafe {
654 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
655 };
656 if ret != 0 {
657 Ok(buffer.to_string())
658 } else {
659 Err(MaaError::FrameworkError(0))
660 }
661 }
662
663 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
667 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
668 common::MaaStatus(s)
669 }
670
671 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
673 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
674 common::MaaStatus(s)
675 }
676
677 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
681 let mut val = long_side;
682 let ret = unsafe {
683 sys::MaaControllerSetOption(
684 self.inner.handle.as_ptr(),
685 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
686 &mut val as *mut _ as *mut c_void,
687 std::mem::size_of::<i32>() as u64,
688 )
689 };
690 common::check_bool(ret)
691 }
692
693 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
695 let mut val = short_side;
696 let ret = unsafe {
697 sys::MaaControllerSetOption(
698 self.inner.handle.as_ptr(),
699 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
700 &mut val as *mut _ as *mut c_void,
701 std::mem::size_of::<i32>() as u64,
702 )
703 };
704 common::check_bool(ret)
705 }
706
707 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
709 let mut val: u8 = if enable { 1 } else { 0 };
710 let ret = unsafe {
711 sys::MaaControllerSetOption(
712 self.inner.handle.as_ptr(),
713 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
714 &mut val as *mut _ as *mut c_void,
715 std::mem::size_of::<u8>() as u64,
716 )
717 };
718 common::check_bool(ret)
719 }
720
721 pub fn set_screenshot_resize_method(&self, method: i32) -> MaaResult<()> {
730 let mut val = method;
731 let ret = unsafe {
732 sys::MaaControllerSetOption(
733 self.inner.handle.as_ptr(),
734 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotResizeMethod as i32,
735 &mut val as *mut _ as *mut c_void,
736 std::mem::size_of::<i32>() as u64,
737 )
738 };
739 common::check_bool(ret)
740 }
741
742 pub fn set_mouse_lock_follow(&self, enable: bool) -> MaaResult<()> {
747 let mut val: u8 = if enable { 1 } else { 0 };
748 let ret = unsafe {
749 sys::MaaControllerSetOption(
750 self.inner.handle.as_ptr(),
751 sys::MaaCtrlOptionEnum_MaaCtrlOption_MouseLockFollow as i32,
752 &mut val as *mut _ as *mut c_void,
753 std::mem::size_of::<u8>() as u64,
754 )
755 };
756 common::check_bool(ret)
757 }
758
759 pub fn set_background_managed_keys(&self, keys: &[i32]) -> MaaResult<()> {
767 let ret = unsafe {
768 sys::MaaControllerSetOption(
769 self.inner.handle.as_ptr(),
770 sys::MaaCtrlOptionEnum_MaaCtrlOption_BackgroundManagedKeys as i32,
771 keys.as_ptr() as *mut c_void,
772 std::mem::size_of_val(keys) as u64,
773 )
774 };
775 common::check_bool(ret)
776 }
777
778 pub fn new_dbg(read_path: &str) -> MaaResult<Self> {
781 let c_read = CString::new(read_path)?;
782 let handle = unsafe { sys::MaaDbgControllerCreate(c_read.as_ptr()) };
783 Self::from_handle(handle)
784 }
785
786 pub fn new_replay(recording_path: &str) -> MaaResult<Self> {
788 let c_recording = CString::new(recording_path)?;
789 let handle = unsafe { sys::MaaReplayControllerCreate(c_recording.as_ptr()) };
790 Self::from_handle(handle)
791 }
792
793 pub fn new_record(inner: &Controller, recording_path: &str) -> MaaResult<Self> {
795 let c_recording = CString::new(recording_path)?;
796 let handle = unsafe { sys::MaaRecordControllerCreate(inner.raw(), c_recording.as_ptr()) };
797
798 if let Some(ptr) = NonNull::new(handle) {
799 Ok(Self::new_with_retained(ptr, vec![Arc::clone(&inner.inner)]))
800 } else {
801 Err(MaaError::FrameworkError(-1))
802 }
803 }
804
805 #[cfg(feature = "win32")]
807 pub fn new_gamepad(
808 hwnd: *mut c_void,
809 gamepad_type: crate::common::GamepadType,
810 screencap_method: crate::common::Win32ScreencapMethod,
811 ) -> MaaResult<Self> {
812 let handle = unsafe {
813 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
814 };
815 Self::from_handle(handle)
816 }
817
818 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
822 where
823 F: Fn(&str, &str) + Send + Sync + 'static,
824 {
825 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
826 let sink_id =
827 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
828 if sink_id != 0 {
829 self.inner
830 .callbacks
831 .lock()
832 .unwrap()
833 .insert(sink_id, cb_arg as usize);
834 Ok(sink_id)
835 } else {
836 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
837 Err(MaaError::FrameworkError(0))
838 }
839 }
840
841 pub fn add_event_sink(
853 &self,
854 sink: Box<dyn crate::event_sink::EventSink>,
855 ) -> MaaResult<sys::MaaSinkId> {
856 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
857 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
858 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
859 if id > 0 {
860 self.inner
861 .event_sinks
862 .lock()
863 .unwrap()
864 .insert(id, arg as usize);
865 Ok(id)
866 } else {
867 unsafe { crate::callback::EventCallback::drop_sink(arg) };
868 Err(MaaError::FrameworkError(0))
869 }
870 }
871
872 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
873 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
874 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
875 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
876 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
877 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
878 }
879 }
880
881 pub fn clear_sinks(&self) {
882 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
883 let mut callbacks = self.inner.callbacks.lock().unwrap();
884 for (_, ptr) in callbacks.drain() {
885 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
886 }
887 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
888 for (_, ptr) in event_sinks.drain() {
889 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
890 }
891 }
892}
893
894impl Drop for ControllerInner {
895 fn drop(&mut self) {
896 unsafe {
897 if self.owns_handle {
898 sys::MaaControllerClearSinks(self.handle.as_ptr());
899 let mut callbacks = self.callbacks.lock().unwrap();
900 for (_, ptr) in callbacks.drain() {
901 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
902 }
903 let mut event_sinks = self.event_sinks.lock().unwrap();
904 for (_, ptr) in event_sinks.drain() {
905 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
906 }
907 sys::MaaControllerDestroy(self.handle.as_ptr());
908 }
909 }
910 }
911}
912
913#[cfg(feature = "adb")]
917pub struct AdbControllerBuilder {
918 adb_path: String,
919 address: String,
920 screencap_methods: sys::MaaAdbScreencapMethod,
921 input_methods: sys::MaaAdbInputMethod,
922 config: String,
923 agent_path: String,
924}
925
926#[cfg(feature = "adb")]
927impl AdbControllerBuilder {
928 pub fn new(adb_path: &str, address: &str) -> Self {
930 Self {
931 adb_path: adb_path.to_string(),
932 address: address.to_string(),
933 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
934 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
935 config: "{}".to_string(),
936 agent_path: String::new(),
937 }
938 }
939
940 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
942 self.screencap_methods = methods;
943 self
944 }
945
946 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
948 self.input_methods = methods;
949 self
950 }
951
952 pub fn config(mut self, config: &str) -> Self {
954 self.config = config.to_string();
955 self
956 }
957
958 pub fn agent_path(mut self, path: &str) -> Self {
960 self.agent_path = path.to_string();
961 self
962 }
963
964 pub fn build(self) -> MaaResult<Controller> {
966 Controller::create_adb(
967 &self.adb_path,
968 &self.address,
969 self.screencap_methods,
970 self.input_methods,
971 &self.config,
972 &self.agent_path,
973 )
974 }
975}
976
977pub struct ControllerRef<'a> {
983 handle: *mut sys::MaaController,
984 _marker: std::marker::PhantomData<&'a ()>,
985}
986
987impl<'a> std::fmt::Debug for ControllerRef<'a> {
988 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
989 f.debug_struct("ControllerRef")
990 .field("handle", &self.handle)
991 .finish()
992 }
993}
994
995impl<'a> ControllerRef<'a> {
996 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
997 if handle.is_null() {
998 None
999 } else {
1000 Some(Self {
1001 handle,
1002 _marker: std::marker::PhantomData,
1003 })
1004 }
1005 }
1006
1007 pub fn connected(&self) -> bool {
1009 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
1010 }
1011
1012 pub fn uuid(&self) -> MaaResult<String> {
1014 let buffer = crate::buffer::MaaStringBuffer::new()?;
1015 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
1016 if ret != 0 {
1017 Ok(buffer.to_string())
1018 } else {
1019 Err(MaaError::FrameworkError(0))
1020 }
1021 }
1022
1023 pub fn info(&self) -> MaaResult<serde_json::Value> {
1025 let buffer = crate::buffer::MaaStringBuffer::new()?;
1026 let ret = unsafe { sys::MaaControllerGetInfo(self.handle, buffer.as_ptr()) };
1027 if ret != 0 {
1028 serde_json::from_str(&buffer.to_string()).map_err(|e| {
1029 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
1030 })
1031 } else {
1032 Err(MaaError::FrameworkError(0))
1033 }
1034 }
1035
1036 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
1038 let mut width: i32 = 0;
1039 let mut height: i32 = 0;
1040 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
1041 if ret != 0 {
1042 Ok((width, height))
1043 } else {
1044 Err(MaaError::FrameworkError(0))
1045 }
1046 }
1047
1048 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1050 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
1051 common::MaaStatus(s)
1052 }
1053
1054 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1056 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
1057 common::MaaStatus(s)
1058 }
1059
1060 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
1062 let buffer = crate::buffer::MaaImageBuffer::new()?;
1063 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
1064 if ret != 0 {
1065 Ok(buffer)
1066 } else {
1067 Err(MaaError::FrameworkError(0))
1068 }
1069 }
1070
1071 pub fn raw(&self) -> *mut sys::MaaController {
1073 self.handle
1074 }
1075}