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 let c_node = CString::new(device_node)?;
245 let handle =
246 unsafe { sys::MaaKWinControllerCreate(c_node.as_ptr(), screen_width, screen_height) };
247
248 Self::from_handle(handle)
249 }
250
251 #[cfg(feature = "custom")]
253 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
254 callback: T,
255 ) -> MaaResult<Self> {
256 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
257 Box::new(Box::new(callback));
258 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
259 let callbacks = crate::custom_controller::get_callbacks();
260 let handle =
261 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
262
263 NonNull::new(handle).map(Self::new_owned).ok_or_else(|| {
264 unsafe {
265 let _ = Box::from_raw(
266 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
267 );
268 }
269 MaaError::FrameworkError(-1)
270 })
271 }
272
273 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
275 if let Some(ptr) = NonNull::new(handle) {
276 Ok(Self::new_owned(ptr))
277 } else {
278 Err(MaaError::FrameworkError(-1))
279 }
280 }
281
282 fn new_owned(handle: NonNull<sys::MaaController>) -> Self {
283 Self::new_with_retained(handle, Vec::new())
284 }
285
286 fn new_with_retained(
287 handle: NonNull<sys::MaaController>,
288 retained_handles: Vec<Arc<ControllerInner>>,
289 ) -> Self {
290 Self {
291 inner: Arc::new(ControllerInner {
292 handle,
293 owns_handle: true,
294 _retained_handles: retained_handles,
295 callbacks: Mutex::new(HashMap::new()),
296 event_sinks: Mutex::new(HashMap::new()),
297 }),
298 }
299 }
300
301 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
303 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
304 Ok(id)
305 }
306
307 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
309 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
310 Ok(id)
311 }
312
313 pub fn post_click_v2(
320 &self,
321 x: i32,
322 y: i32,
323 contact: i32,
324 pressure: i32,
325 ) -> MaaResult<common::MaaId> {
326 let id = unsafe {
327 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
328 };
329 Ok(id)
330 }
331
332 pub fn post_swipe(
339 &self,
340 x1: i32,
341 y1: i32,
342 x2: i32,
343 y2: i32,
344 duration: i32,
345 ) -> MaaResult<common::MaaId> {
346 let id = unsafe {
347 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
348 };
349 Ok(id)
350 }
351
352 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
357 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
358 Ok(id)
359 }
360
361 #[deprecated(note = "Use post_click_key instead")]
363 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
364 self.post_click_key(keycode)
365 }
366
367 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
372 let c_text = CString::new(text)?;
373 let id =
374 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
375 Ok(id)
376 }
377
378 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
384 let c_cmd = CString::new(cmd)?;
385 let id = unsafe {
386 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
387 };
388 Ok(id)
389 }
390
391 pub fn post_touch_down(
398 &self,
399 contact: i32,
400 x: i32,
401 y: i32,
402 pressure: i32,
403 ) -> MaaResult<common::MaaId> {
404 let id = unsafe {
405 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
406 };
407 Ok(id)
408 }
409
410 pub fn post_touch_move(
417 &self,
418 contact: i32,
419 x: i32,
420 y: i32,
421 pressure: i32,
422 ) -> MaaResult<common::MaaId> {
423 let id = unsafe {
424 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
425 };
426 Ok(id)
427 }
428
429 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
434 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
435 Ok(id)
436 }
437
438 pub fn post_relative_move(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
444 let id = unsafe { sys::MaaControllerPostRelativeMove(self.inner.handle.as_ptr(), dx, dy) };
445 Ok(id)
446 }
447
448 #[inline]
450 pub fn raw(&self) -> *mut sys::MaaController {
451 self.inner.handle.as_ptr()
452 }
453
454 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
460 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
461 Ok(id)
462 }
463
464 pub fn connected(&self) -> bool {
466 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
467 }
468
469 pub fn uuid(&self) -> MaaResult<String> {
471 let buffer = crate::buffer::MaaStringBuffer::new()?;
472 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
473 if ret != 0 {
474 Ok(buffer.to_string())
475 } else {
476 Err(MaaError::FrameworkError(0))
477 }
478 }
479
480 pub fn info(&self) -> MaaResult<serde_json::Value> {
485 let buffer = crate::buffer::MaaStringBuffer::new()?;
486 let ret = unsafe { sys::MaaControllerGetInfo(self.inner.handle.as_ptr(), buffer.as_ptr()) };
487 if ret != 0 {
488 serde_json::from_str(&buffer.to_string()).map_err(|e| {
489 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
490 })
491 } else {
492 Err(MaaError::FrameworkError(0))
493 }
494 }
495
496 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
498 let mut width: i32 = 0;
499 let mut height: i32 = 0;
500 let ret = unsafe {
501 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
502 };
503 if ret != 0 {
504 Ok((width, height))
505 } else {
506 Err(MaaError::FrameworkError(0))
507 }
508 }
509
510 pub fn post_swipe_v2(
521 &self,
522 x1: i32,
523 y1: i32,
524 x2: i32,
525 y2: i32,
526 duration: i32,
527 contact: i32,
528 pressure: i32,
529 ) -> MaaResult<common::MaaId> {
530 let id = unsafe {
531 sys::MaaControllerPostSwipeV2(
532 self.inner.handle.as_ptr(),
533 x1,
534 y1,
535 x2,
536 y2,
537 duration,
538 contact,
539 pressure,
540 )
541 };
542 Ok(id)
543 }
544
545 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
549 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
550 Ok(id)
551 }
552
553 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
555 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
556 Ok(id)
557 }
558
559 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
566 let c_intent = CString::new(intent)?;
567 let id = unsafe {
568 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
569 };
570 Ok(id)
571 }
572
573 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
578 let c_intent = CString::new(intent)?;
579 let id =
580 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
581 Ok(id)
582 }
583
584 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
592 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
593 Ok(id)
594 }
595
596 pub fn post_inactive(&self) -> MaaResult<common::MaaId> {
603 let id = unsafe { sys::MaaControllerPostInactive(self.inner.handle.as_ptr()) };
604 Ok(id)
605 }
606
607 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
611 let buffer = crate::buffer::MaaImageBuffer::new()?;
612 let ret =
613 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
614 if ret != 0 {
615 Ok(buffer)
616 } else {
617 Err(MaaError::FrameworkError(0))
618 }
619 }
620
621 pub fn shell_output(&self) -> MaaResult<String> {
625 let buffer = crate::buffer::MaaStringBuffer::new()?;
626 let ret = unsafe {
627 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
628 };
629 if ret != 0 {
630 Ok(buffer.to_string())
631 } else {
632 Err(MaaError::FrameworkError(0))
633 }
634 }
635
636 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
640 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
641 common::MaaStatus(s)
642 }
643
644 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
646 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
647 common::MaaStatus(s)
648 }
649
650 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
654 let mut val = long_side;
655 let ret = unsafe {
656 sys::MaaControllerSetOption(
657 self.inner.handle.as_ptr(),
658 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
659 &mut val as *mut _ as *mut c_void,
660 std::mem::size_of::<i32>() as u64,
661 )
662 };
663 common::check_bool(ret)
664 }
665
666 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
668 let mut val = short_side;
669 let ret = unsafe {
670 sys::MaaControllerSetOption(
671 self.inner.handle.as_ptr(),
672 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
673 &mut val as *mut _ as *mut c_void,
674 std::mem::size_of::<i32>() as u64,
675 )
676 };
677 common::check_bool(ret)
678 }
679
680 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
682 let mut val: u8 = if enable { 1 } else { 0 };
683 let ret = unsafe {
684 sys::MaaControllerSetOption(
685 self.inner.handle.as_ptr(),
686 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
687 &mut val as *mut _ as *mut c_void,
688 std::mem::size_of::<u8>() as u64,
689 )
690 };
691 common::check_bool(ret)
692 }
693
694 pub fn set_screenshot_resize_method(&self, method: i32) -> MaaResult<()> {
703 let mut val = method;
704 let ret = unsafe {
705 sys::MaaControllerSetOption(
706 self.inner.handle.as_ptr(),
707 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotResizeMethod as i32,
708 &mut val as *mut _ as *mut c_void,
709 std::mem::size_of::<i32>() as u64,
710 )
711 };
712 common::check_bool(ret)
713 }
714
715 pub fn set_mouse_lock_follow(&self, enable: bool) -> MaaResult<()> {
720 let mut val: u8 = if enable { 1 } else { 0 };
721 let ret = unsafe {
722 sys::MaaControllerSetOption(
723 self.inner.handle.as_ptr(),
724 sys::MaaCtrlOptionEnum_MaaCtrlOption_MouseLockFollow as i32,
725 &mut val as *mut _ as *mut c_void,
726 std::mem::size_of::<u8>() as u64,
727 )
728 };
729 common::check_bool(ret)
730 }
731
732 pub fn set_background_managed_keys(&self, keys: &[i32]) -> MaaResult<()> {
740 let ret = unsafe {
741 sys::MaaControllerSetOption(
742 self.inner.handle.as_ptr(),
743 sys::MaaCtrlOptionEnum_MaaCtrlOption_BackgroundManagedKeys as i32,
744 keys.as_ptr() as *mut c_void,
745 std::mem::size_of_val(keys) as u64,
746 )
747 };
748 common::check_bool(ret)
749 }
750
751 pub fn new_dbg(read_path: &str) -> MaaResult<Self> {
754 let c_read = CString::new(read_path)?;
755 let handle = unsafe { sys::MaaDbgControllerCreate(c_read.as_ptr()) };
756 Self::from_handle(handle)
757 }
758
759 pub fn new_replay(recording_path: &str) -> MaaResult<Self> {
761 let c_recording = CString::new(recording_path)?;
762 let handle = unsafe { sys::MaaReplayControllerCreate(c_recording.as_ptr()) };
763 Self::from_handle(handle)
764 }
765
766 pub fn new_record(inner: &Controller, recording_path: &str) -> MaaResult<Self> {
768 let c_recording = CString::new(recording_path)?;
769 let handle = unsafe { sys::MaaRecordControllerCreate(inner.raw(), c_recording.as_ptr()) };
770
771 if let Some(ptr) = NonNull::new(handle) {
772 Ok(Self::new_with_retained(ptr, vec![Arc::clone(&inner.inner)]))
773 } else {
774 Err(MaaError::FrameworkError(-1))
775 }
776 }
777
778 #[cfg(feature = "win32")]
780 pub fn new_gamepad(
781 hwnd: *mut c_void,
782 gamepad_type: crate::common::GamepadType,
783 screencap_method: crate::common::Win32ScreencapMethod,
784 ) -> MaaResult<Self> {
785 let handle = unsafe {
786 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
787 };
788 Self::from_handle(handle)
789 }
790
791 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
795 where
796 F: Fn(&str, &str) + Send + Sync + 'static,
797 {
798 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
799 let sink_id =
800 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
801 if sink_id != 0 {
802 self.inner
803 .callbacks
804 .lock()
805 .unwrap()
806 .insert(sink_id, cb_arg as usize);
807 Ok(sink_id)
808 } else {
809 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
810 Err(MaaError::FrameworkError(0))
811 }
812 }
813
814 pub fn add_event_sink(
826 &self,
827 sink: Box<dyn crate::event_sink::EventSink>,
828 ) -> MaaResult<sys::MaaSinkId> {
829 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
830 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
831 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
832 if id > 0 {
833 self.inner
834 .event_sinks
835 .lock()
836 .unwrap()
837 .insert(id, arg as usize);
838 Ok(id)
839 } else {
840 unsafe { crate::callback::EventCallback::drop_sink(arg) };
841 Err(MaaError::FrameworkError(0))
842 }
843 }
844
845 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
846 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
847 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
848 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
849 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
850 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
851 }
852 }
853
854 pub fn clear_sinks(&self) {
855 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
856 let mut callbacks = self.inner.callbacks.lock().unwrap();
857 for (_, ptr) in callbacks.drain() {
858 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
859 }
860 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
861 for (_, ptr) in event_sinks.drain() {
862 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
863 }
864 }
865}
866
867impl Drop for ControllerInner {
868 fn drop(&mut self) {
869 unsafe {
870 if self.owns_handle {
871 sys::MaaControllerClearSinks(self.handle.as_ptr());
872 let mut callbacks = self.callbacks.lock().unwrap();
873 for (_, ptr) in callbacks.drain() {
874 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
875 }
876 let mut event_sinks = self.event_sinks.lock().unwrap();
877 for (_, ptr) in event_sinks.drain() {
878 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
879 }
880 sys::MaaControllerDestroy(self.handle.as_ptr());
881 }
882 }
883 }
884}
885
886#[cfg(feature = "adb")]
890pub struct AdbControllerBuilder {
891 adb_path: String,
892 address: String,
893 screencap_methods: sys::MaaAdbScreencapMethod,
894 input_methods: sys::MaaAdbInputMethod,
895 config: String,
896 agent_path: String,
897}
898
899#[cfg(feature = "adb")]
900impl AdbControllerBuilder {
901 pub fn new(adb_path: &str, address: &str) -> Self {
903 Self {
904 adb_path: adb_path.to_string(),
905 address: address.to_string(),
906 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
907 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
908 config: "{}".to_string(),
909 agent_path: String::new(),
910 }
911 }
912
913 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
915 self.screencap_methods = methods;
916 self
917 }
918
919 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
921 self.input_methods = methods;
922 self
923 }
924
925 pub fn config(mut self, config: &str) -> Self {
927 self.config = config.to_string();
928 self
929 }
930
931 pub fn agent_path(mut self, path: &str) -> Self {
933 self.agent_path = path.to_string();
934 self
935 }
936
937 pub fn build(self) -> MaaResult<Controller> {
939 Controller::create_adb(
940 &self.adb_path,
941 &self.address,
942 self.screencap_methods,
943 self.input_methods,
944 &self.config,
945 &self.agent_path,
946 )
947 }
948}
949
950pub struct ControllerRef<'a> {
956 handle: *mut sys::MaaController,
957 _marker: std::marker::PhantomData<&'a ()>,
958}
959
960impl<'a> std::fmt::Debug for ControllerRef<'a> {
961 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
962 f.debug_struct("ControllerRef")
963 .field("handle", &self.handle)
964 .finish()
965 }
966}
967
968impl<'a> ControllerRef<'a> {
969 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
970 if handle.is_null() {
971 None
972 } else {
973 Some(Self {
974 handle,
975 _marker: std::marker::PhantomData,
976 })
977 }
978 }
979
980 pub fn connected(&self) -> bool {
982 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
983 }
984
985 pub fn uuid(&self) -> MaaResult<String> {
987 let buffer = crate::buffer::MaaStringBuffer::new()?;
988 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
989 if ret != 0 {
990 Ok(buffer.to_string())
991 } else {
992 Err(MaaError::FrameworkError(0))
993 }
994 }
995
996 pub fn info(&self) -> MaaResult<serde_json::Value> {
998 let buffer = crate::buffer::MaaStringBuffer::new()?;
999 let ret = unsafe { sys::MaaControllerGetInfo(self.handle, buffer.as_ptr()) };
1000 if ret != 0 {
1001 serde_json::from_str(&buffer.to_string()).map_err(|e| {
1002 MaaError::InvalidArgument(format!("Failed to parse controller info: {}", e))
1003 })
1004 } else {
1005 Err(MaaError::FrameworkError(0))
1006 }
1007 }
1008
1009 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
1011 let mut width: i32 = 0;
1012 let mut height: i32 = 0;
1013 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
1014 if ret != 0 {
1015 Ok((width, height))
1016 } else {
1017 Err(MaaError::FrameworkError(0))
1018 }
1019 }
1020
1021 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1023 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
1024 common::MaaStatus(s)
1025 }
1026
1027 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
1029 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
1030 common::MaaStatus(s)
1031 }
1032
1033 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
1035 let buffer = crate::buffer::MaaImageBuffer::new()?;
1036 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
1037 if ret != 0 {
1038 Ok(buffer)
1039 } else {
1040 Err(MaaError::FrameworkError(0))
1041 }
1042 }
1043
1044 pub fn raw(&self) -> *mut sys::MaaController {
1046 self.handle
1047 }
1048}