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