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> {
192 let c_addr = CString::new(address)?;
193 let c_uuid = CString::new(uuid)?;
194 let handle = unsafe { sys::MaaPlayCoverControllerCreate(c_addr.as_ptr(), c_uuid.as_ptr()) };
195
196 Self::from_handle(handle)
197 }
198
199 pub fn new_linux<T: Serialize>(config: &T) -> MaaResult<Self> {
207 let config_json = serde_json::to_string(config).map_err(|e| {
208 MaaError::InvalidConfig(format!(
209 "Failed to serialize Linux controller config: {}",
210 e
211 ))
212 })?;
213 let c_config = CString::new(config_json)?;
214 let handle = unsafe { sys::MaaLinuxControllerCreate(c_config.as_ptr()) };
215
216 Self::from_handle(handle)
217 }
218
219 #[allow(deprecated)]
224 #[deprecated(note = "Use `Controller::new_linux` instead")]
225 pub fn new_wlroots(wlr_socket_path: &str) -> MaaResult<Self> {
226 Self::new_wlroots_with_vk_code(wlr_socket_path, false)
227 }
228
229 #[deprecated(note = "Use `Controller::new_linux` instead")]
236 pub fn new_wlroots_with_vk_code(
237 wlr_socket_path: &str,
238 use_win32_vk_code: bool,
239 ) -> MaaResult<Self> {
240 let c_path = CString::new(wlr_socket_path)?;
241 let handle = unsafe {
242 sys::MaaWlRootsControllerCreate(c_path.as_ptr(), use_win32_vk_code as sys::MaaBool)
243 };
244
245 Self::from_handle(handle)
246 }
247
248 #[cfg(target_os = "linux")]
269 #[allow(deprecated)]
270 #[deprecated(note = "Use `Controller::new_linux` instead")]
271 pub fn new_kwin(device_node: &str, screen_width: i32, screen_height: i32) -> MaaResult<Self> {
272 Self::new_kwin_with_vk_code(device_node, screen_width, screen_height, false)
273 }
274
275 #[cfg(target_os = "linux")]
287 #[deprecated(note = "Use `Controller::new_linux` instead")]
288 pub fn new_kwin_with_vk_code(
289 device_node: &str,
290 screen_width: i32,
291 screen_height: i32,
292 use_win32_vk_code: bool,
293 ) -> MaaResult<Self> {
294 let c_node = CString::new(device_node)?;
295 let handle = unsafe {
296 sys::MaaKWinControllerCreate(
297 c_node.as_ptr(),
298 screen_width,
299 screen_height,
300 use_win32_vk_code as sys::MaaBool,
301 )
302 };
303
304 Self::from_handle(handle)
305 }
306
307 #[cfg(feature = "custom")]
309 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
310 callback: T,
311 ) -> MaaResult<Self> {
312 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
313 Box::new(Box::new(callback));
314 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
315 let callbacks = crate::custom_controller::get_callbacks();
316 let handle =
317 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
318
319 NonNull::new(handle).map(Self::new_owned).ok_or_else(|| {
320 unsafe {
321 let _ = Box::from_raw(
322 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
323 );
324 }
325 MaaError::FrameworkError(-1)
326 })
327 }
328
329 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
331 if let Some(ptr) = NonNull::new(handle) {
332 Ok(Self::new_owned(ptr))
333 } else {
334 Err(MaaError::FrameworkError(-1))
335 }
336 }
337
338 fn new_owned(handle: NonNull<sys::MaaController>) -> Self {
339 Self::new_with_retained(handle, Vec::new())
340 }
341
342 fn new_with_retained(
343 handle: NonNull<sys::MaaController>,
344 retained_handles: Vec<Arc<ControllerInner>>,
345 ) -> Self {
346 Self {
347 inner: Arc::new(ControllerInner {
348 handle,
349 owns_handle: true,
350 _retained_handles: retained_handles,
351 callbacks: Mutex::new(HashMap::new()),
352 event_sinks: Mutex::new(HashMap::new()),
353 }),
354 }
355 }
356
357 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
359 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
360 Ok(id)
361 }
362
363 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
365 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
366 Ok(id)
367 }
368
369 pub fn post_click_v2(
376 &self,
377 x: i32,
378 y: i32,
379 contact: i32,
380 pressure: i32,
381 ) -> MaaResult<common::MaaId> {
382 let id = unsafe {
383 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
384 };
385 Ok(id)
386 }
387
388 pub fn post_swipe(
395 &self,
396 x1: i32,
397 y1: i32,
398 x2: i32,
399 y2: i32,
400 duration: i32,
401 ) -> MaaResult<common::MaaId> {
402 let id = unsafe {
403 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
404 };
405 Ok(id)
406 }
407
408 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
413 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
414 Ok(id)
415 }
416
417 #[deprecated(note = "Use post_click_key instead")]
419 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
420 self.post_click_key(keycode)
421 }
422
423 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
428 let c_text = CString::new(text)?;
429 let id =
430 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
431 Ok(id)
432 }
433
434 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
440 let c_cmd = CString::new(cmd)?;
441 let id = unsafe {
442 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
443 };
444 Ok(id)
445 }
446
447 pub fn post_touch_down(
454 &self,
455 contact: i32,
456 x: i32,
457 y: i32,
458 pressure: i32,
459 ) -> MaaResult<common::MaaId> {
460 let id = unsafe {
461 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
462 };
463 Ok(id)
464 }
465
466 pub fn post_touch_move(
473 &self,
474 contact: i32,
475 x: i32,
476 y: i32,
477 pressure: i32,
478 ) -> MaaResult<common::MaaId> {
479 let id = unsafe {
480 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
481 };
482 Ok(id)
483 }
484
485 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
490 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
491 Ok(id)
492 }
493
494 pub fn post_relative_move(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
500 let id = unsafe { sys::MaaControllerPostRelativeMove(self.inner.handle.as_ptr(), dx, dy) };
501 Ok(id)
502 }
503
504 #[inline]
506 pub fn raw(&self) -> *mut sys::MaaController {
507 self.inner.handle.as_ptr()
508 }
509
510 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
516 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
517 Ok(id)
518 }
519
520 pub fn connected(&self) -> bool {
522 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
523 }
524
525 pub fn uuid(&self) -> MaaResult<String> {
527 let buffer = crate::buffer::MaaStringBuffer::new()?;
528 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
529 if ret != 0 {
530 Ok(buffer.to_string())
531 } else {
532 Err(MaaError::FrameworkError(0))
533 }
534 }
535
536 pub fn info(&self) -> MaaResult<serde_json::Value> {
541 let buffer = crate::buffer::MaaStringBuffer::new()?;
542 let ret = unsafe { sys::MaaControllerGetInfo(self.inner.handle.as_ptr(), buffer.as_ptr()) };
543 if ret != 0 {
544 serde_json::from_str(&buffer.to_string()).map_err(|e| {
545 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
546 })
547 } else {
548 Err(MaaError::FrameworkError(0))
549 }
550 }
551
552 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
554 let mut width: i32 = 0;
555 let mut height: i32 = 0;
556 let ret = unsafe {
557 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
558 };
559 if ret != 0 {
560 Ok((width, height))
561 } else {
562 Err(MaaError::FrameworkError(0))
563 }
564 }
565
566 pub fn post_swipe_v2(
577 &self,
578 x1: i32,
579 y1: i32,
580 x2: i32,
581 y2: i32,
582 duration: i32,
583 contact: i32,
584 pressure: i32,
585 ) -> MaaResult<common::MaaId> {
586 let id = unsafe {
587 sys::MaaControllerPostSwipeV2(
588 self.inner.handle.as_ptr(),
589 x1,
590 y1,
591 x2,
592 y2,
593 duration,
594 contact,
595 pressure,
596 )
597 };
598 Ok(id)
599 }
600
601 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
605 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
606 Ok(id)
607 }
608
609 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
611 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
612 Ok(id)
613 }
614
615 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
622 let c_intent = CString::new(intent)?;
623 let id = unsafe {
624 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
625 };
626 Ok(id)
627 }
628
629 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
635 let c_intent = CString::new(intent)?;
636 let id =
637 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
638 Ok(id)
639 }
640
641 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
649 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
650 Ok(id)
651 }
652
653 pub fn post_inactive(&self) -> MaaResult<common::MaaId> {
660 let id = unsafe { sys::MaaControllerPostInactive(self.inner.handle.as_ptr()) };
661 Ok(id)
662 }
663
664 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
668 let buffer = crate::buffer::MaaImageBuffer::new()?;
669 let ret =
670 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
671 if ret != 0 {
672 Ok(buffer)
673 } else {
674 Err(MaaError::FrameworkError(0))
675 }
676 }
677
678 pub fn shell_output(&self) -> MaaResult<String> {
682 let buffer = crate::buffer::MaaStringBuffer::new()?;
683 let ret = unsafe {
684 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
685 };
686 if ret != 0 {
687 Ok(buffer.to_string())
688 } else {
689 Err(MaaError::FrameworkError(0))
690 }
691 }
692
693 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
697 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
698 common::MaaStatus(s)
699 }
700
701 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
703 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
704 common::MaaStatus(s)
705 }
706
707 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
713 let mut val = long_side;
714 let ret = unsafe {
715 sys::MaaControllerSetOption(
716 self.inner.handle.as_ptr(),
717 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
718 &mut val as *mut _ as *mut c_void,
719 std::mem::size_of::<i32>() as u64,
720 )
721 };
722 common::check_bool(ret)
723 }
724
725 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
729 let mut val = short_side;
730 let ret = unsafe {
731 sys::MaaControllerSetOption(
732 self.inner.handle.as_ptr(),
733 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
734 &mut val as *mut _ as *mut c_void,
735 std::mem::size_of::<i32>() as u64,
736 )
737 };
738 common::check_bool(ret)
739 }
740
741 pub fn set_screenshot_target_expand(&self, width: i32, height: i32) -> MaaResult<()> {
747 let mut size = [width, height];
748 let ret = unsafe {
749 sys::MaaControllerSetOption(
750 self.inner.handle.as_ptr(),
751 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetExpand as i32,
752 size.as_mut_ptr().cast(),
753 std::mem::size_of_val(&size) as u64,
754 )
755 };
756 common::check_bool(ret)
757 }
758
759 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
761 let mut val: u8 = if enable { 1 } else { 0 };
762 let ret = unsafe {
763 sys::MaaControllerSetOption(
764 self.inner.handle.as_ptr(),
765 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
766 &mut val as *mut _ as *mut c_void,
767 std::mem::size_of::<u8>() as u64,
768 )
769 };
770 common::check_bool(ret)
771 }
772
773 pub fn set_screenshot_resize_method(&self, method: i32) -> MaaResult<()> {
782 let mut val = method;
783 let ret = unsafe {
784 sys::MaaControllerSetOption(
785 self.inner.handle.as_ptr(),
786 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotResizeMethod as i32,
787 &mut val as *mut _ as *mut c_void,
788 std::mem::size_of::<i32>() as u64,
789 )
790 };
791 common::check_bool(ret)
792 }
793
794 pub fn set_mouse_lock_follow(&self, enable: bool) -> MaaResult<()> {
799 let mut val: u8 = if enable { 1 } else { 0 };
800 let ret = unsafe {
801 sys::MaaControllerSetOption(
802 self.inner.handle.as_ptr(),
803 sys::MaaCtrlOptionEnum_MaaCtrlOption_MouseLockFollow as i32,
804 &mut val as *mut _ as *mut c_void,
805 std::mem::size_of::<u8>() as u64,
806 )
807 };
808 common::check_bool(ret)
809 }
810
811 pub fn set_background_managed_keys(&self, keys: &[i32]) -> MaaResult<()> {
819 let ret = unsafe {
820 sys::MaaControllerSetOption(
821 self.inner.handle.as_ptr(),
822 sys::MaaCtrlOptionEnum_MaaCtrlOption_BackgroundManagedKeys as i32,
823 keys.as_ptr() as *mut c_void,
824 std::mem::size_of_val(keys) as u64,
825 )
826 };
827 common::check_bool(ret)
828 }
829
830 pub fn new_dbg(read_path: &str) -> MaaResult<Self> {
833 let c_read = CString::new(read_path)?;
834 let handle = unsafe { sys::MaaDbgControllerCreate(c_read.as_ptr()) };
835 Self::from_handle(handle)
836 }
837
838 pub fn new_replay(recording_path: &str) -> MaaResult<Self> {
840 let c_recording = CString::new(recording_path)?;
841 let handle = unsafe { sys::MaaReplayControllerCreate(c_recording.as_ptr()) };
842 Self::from_handle(handle)
843 }
844
845 pub fn new_record(inner: &Controller, recording_path: &str) -> MaaResult<Self> {
847 let c_recording = CString::new(recording_path)?;
848 let handle = unsafe { sys::MaaRecordControllerCreate(inner.raw(), c_recording.as_ptr()) };
849
850 if let Some(ptr) = NonNull::new(handle) {
851 Ok(Self::new_with_retained(ptr, vec![Arc::clone(&inner.inner)]))
852 } else {
853 Err(MaaError::FrameworkError(-1))
854 }
855 }
856
857 #[cfg(feature = "win32")]
859 pub fn new_gamepad(
860 hwnd: *mut c_void,
861 gamepad_type: crate::common::GamepadType,
862 screencap_method: crate::common::Win32ScreencapMethod,
863 ) -> MaaResult<Self> {
864 let handle = unsafe {
865 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
866 };
867 Self::from_handle(handle)
868 }
869
870 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
874 where
875 F: Fn(&str, &str) + Send + Sync + 'static,
876 {
877 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
878 let sink_id =
879 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
880 if sink_id != 0 {
881 self.inner
882 .callbacks
883 .lock()
884 .unwrap()
885 .insert(sink_id, cb_arg as usize);
886 Ok(sink_id)
887 } else {
888 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
889 Err(MaaError::FrameworkError(0))
890 }
891 }
892
893 pub fn add_event_sink(
905 &self,
906 sink: Box<dyn crate::event_sink::EventSink>,
907 ) -> MaaResult<sys::MaaSinkId> {
908 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
909 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
910 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
911 if id > 0 {
912 self.inner
913 .event_sinks
914 .lock()
915 .unwrap()
916 .insert(id, arg as usize);
917 Ok(id)
918 } else {
919 unsafe { crate::callback::EventCallback::drop_sink(arg) };
920 Err(MaaError::FrameworkError(0))
921 }
922 }
923
924 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
925 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
926 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
927 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
928 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
929 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
930 }
931 }
932
933 pub fn clear_sinks(&self) {
934 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
935 let mut callbacks = self.inner.callbacks.lock().unwrap();
936 for (_, ptr) in callbacks.drain() {
937 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
938 }
939 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
940 for (_, ptr) in event_sinks.drain() {
941 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
942 }
943 }
944}
945
946impl Drop for ControllerInner {
947 fn drop(&mut self) {
948 unsafe {
949 if self.owns_handle {
950 sys::MaaControllerClearSinks(self.handle.as_ptr());
951 let mut callbacks = self.callbacks.lock().unwrap();
952 for (_, ptr) in callbacks.drain() {
953 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
954 }
955 let mut event_sinks = self.event_sinks.lock().unwrap();
956 for (_, ptr) in event_sinks.drain() {
957 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
958 }
959 sys::MaaControllerDestroy(self.handle.as_ptr());
960 }
961 }
962 }
963}
964
965#[cfg(feature = "adb")]
969pub struct AdbControllerBuilder {
970 adb_path: String,
971 address: String,
972 screencap_methods: sys::MaaAdbScreencapMethod,
973 input_methods: sys::MaaAdbInputMethod,
974 config: String,
975 agent_path: String,
976}
977
978#[cfg(feature = "adb")]
979impl AdbControllerBuilder {
980 pub fn new(adb_path: &str, address: &str) -> Self {
982 Self {
983 adb_path: adb_path.to_string(),
984 address: address.to_string(),
985 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
986 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
987 config: "{}".to_string(),
988 agent_path: String::new(),
989 }
990 }
991
992 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
994 self.screencap_methods = methods;
995 self
996 }
997
998 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
1000 self.input_methods = methods;
1001 self
1002 }
1003
1004 pub fn config(mut self, config: &str) -> Self {
1006 self.config = config.to_string();
1007 self
1008 }
1009
1010 pub fn agent_path(mut self, path: &str) -> Self {
1012 self.agent_path = path.to_string();
1013 self
1014 }
1015
1016 pub fn build(self) -> MaaResult<Controller> {
1018 Controller::create_adb(
1019 &self.adb_path,
1020 &self.address,
1021 self.screencap_methods,
1022 self.input_methods,
1023 &self.config,
1024 &self.agent_path,
1025 )
1026 }
1027}
1028
1029pub struct ControllerRef<'a> {
1035 handle: *mut sys::MaaController,
1036 _marker: std::marker::PhantomData<&'a ()>,
1037}
1038
1039impl<'a> std::fmt::Debug for ControllerRef<'a> {
1040 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1041 f.debug_struct("ControllerRef")
1042 .field("handle", &self.handle)
1043 .finish()
1044 }
1045}
1046
1047impl<'a> ControllerRef<'a> {
1048 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
1049 if handle.is_null() {
1050 None
1051 } else {
1052 Some(Self {
1053 handle,
1054 _marker: std::marker::PhantomData,
1055 })
1056 }
1057 }
1058
1059 pub fn connected(&self) -> bool {
1061 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
1062 }
1063
1064 pub fn uuid(&self) -> MaaResult<String> {
1066 let buffer = crate::buffer::MaaStringBuffer::new()?;
1067 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
1068 if ret != 0 {
1069 Ok(buffer.to_string())
1070 } else {
1071 Err(MaaError::FrameworkError(0))
1072 }
1073 }
1074
1075 pub fn info(&self) -> MaaResult<serde_json::Value> {
1077 let buffer = crate::buffer::MaaStringBuffer::new()?;
1078 let ret = unsafe { sys::MaaControllerGetInfo(self.handle, buffer.as_ptr()) };
1079 if ret != 0 {
1080 serde_json::from_str(&buffer.to_string()).map_err(|e| {
1081 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
1082 })
1083 } else {
1084 Err(MaaError::FrameworkError(0))
1085 }
1086 }
1087
1088 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
1090 let mut width: i32 = 0;
1091 let mut height: i32 = 0;
1092 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
1093 if ret != 0 {
1094 Ok((width, height))
1095 } else {
1096 Err(MaaError::FrameworkError(0))
1097 }
1098 }
1099
1100 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1102 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
1103 common::MaaStatus(s)
1104 }
1105
1106 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1108 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
1109 common::MaaStatus(s)
1110 }
1111
1112 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
1114 let buffer = crate::buffer::MaaImageBuffer::new()?;
1115 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
1116 if ret != 0 {
1117 Ok(buffer)
1118 } else {
1119 Err(MaaError::FrameworkError(0))
1120 }
1121 }
1122
1123 pub fn raw(&self) -> *mut sys::MaaController {
1125 self.handle
1126 }
1127}