1use crate::{common, sys, MaaError, MaaResult};
4use std::collections::HashMap;
5use std::ffi::CString;
6use std::os::raw::c_void;
7use std::ptr::NonNull;
8use std::sync::{Arc, Mutex};
9
10#[derive(Clone)]
20pub struct Controller {
21 inner: Arc<ControllerInner>,
22}
23
24struct ControllerInner {
25 handle: NonNull<sys::MaaController>,
26 owns_handle: bool,
27 callbacks: Mutex<HashMap<sys::MaaSinkId, usize>>,
28 event_sinks: Mutex<HashMap<sys::MaaSinkId, usize>>,
29}
30
31unsafe impl Send for ControllerInner {}
32unsafe impl Sync for ControllerInner {}
33
34unsafe impl Send for Controller {}
36unsafe impl Sync for Controller {}
37
38impl std::fmt::Debug for Controller {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("Controller")
41 .field("handle", &self.inner.handle)
42 .finish()
43 }
44}
45
46impl Controller {
47 #[cfg(feature = "adb")]
55 pub fn new_adb(
56 adb_path: &str,
57 address: &str,
58 config: &str,
59 agent_path: &str,
60 ) -> MaaResult<Self> {
61 Self::create_adb(
62 adb_path,
63 address,
64 sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
65 sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
66 config,
67 agent_path,
68 )
69 }
70
71 #[cfg(feature = "adb")]
72 pub(crate) fn create_adb(
73 adb_path: &str,
74 address: &str,
75 screencap_method: sys::MaaAdbScreencapMethod,
76 input_method: sys::MaaAdbInputMethod,
77 config: &str,
78 agent_path: &str,
79 ) -> MaaResult<Self> {
80 let c_adb = CString::new(adb_path)?;
81 let c_addr = CString::new(address)?;
82 let c_cfg = CString::new(config)?;
83 let c_agent = CString::new(agent_path)?;
84
85 let handle = unsafe {
86 sys::MaaAdbControllerCreate(
87 c_adb.as_ptr(),
88 c_addr.as_ptr(),
89 screencap_method,
90 input_method,
91 c_cfg.as_ptr(),
92 c_agent.as_ptr(),
93 )
94 };
95
96 if let Some(ptr) = NonNull::new(handle) {
97 Ok(Self {
98 inner: Arc::new(ControllerInner {
99 handle: ptr,
100 owns_handle: true,
101 callbacks: Mutex::new(HashMap::new()),
102 event_sinks: Mutex::new(HashMap::new()),
103 }),
104 })
105 } else {
106 Err(MaaError::FrameworkError(-1))
107 }
108 }
109
110 #[cfg(feature = "win32")]
112 pub fn new_win32(
113 hwnd: *mut c_void,
114 screencap_method: sys::MaaWin32ScreencapMethod,
115 mouse_method: sys::MaaWin32InputMethod,
116 keyboard_method: sys::MaaWin32InputMethod,
117 ) -> MaaResult<Self> {
118 let handle = unsafe {
119 sys::MaaWin32ControllerCreate(hwnd, screencap_method, mouse_method, keyboard_method)
120 };
121
122 Self::from_handle(handle)
123 }
124
125 pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self> {
128 let c_addr = CString::new(address)?;
129 let c_uuid = CString::new(uuid)?;
130 let handle = unsafe { sys::MaaPlayCoverControllerCreate(c_addr.as_ptr(), c_uuid.as_ptr()) };
131
132 Self::from_handle(handle)
133 }
134
135 #[cfg(feature = "custom")]
137 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
138 callback: T,
139 ) -> MaaResult<Self> {
140 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
141 Box::new(Box::new(callback));
142 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
143 let callbacks = crate::custom_controller::get_callbacks();
144 let handle =
145 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
146
147 NonNull::new(handle)
148 .map(|ptr| Self {
149 inner: Arc::new(ControllerInner {
150 handle: ptr,
151 owns_handle: true,
152 callbacks: Mutex::new(HashMap::new()),
153 event_sinks: Mutex::new(HashMap::new()),
154 }),
155 })
156 .ok_or_else(|| {
157 unsafe {
158 let _ = Box::from_raw(
159 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
160 );
161 }
162 MaaError::FrameworkError(-1)
163 })
164 }
165
166 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
168 if let Some(ptr) = NonNull::new(handle) {
169 Ok(Self {
170 inner: Arc::new(ControllerInner {
171 handle: ptr,
172 owns_handle: true,
173 callbacks: Mutex::new(HashMap::new()),
174 event_sinks: Mutex::new(HashMap::new()),
175 }),
176 })
177 } else {
178 Err(MaaError::FrameworkError(-1))
179 }
180 }
181
182 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
184 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
185 Ok(id)
186 }
187
188 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
190 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
191 Ok(id)
192 }
193
194 pub fn post_click_v2(
201 &self,
202 x: i32,
203 y: i32,
204 contact: i32,
205 pressure: i32,
206 ) -> MaaResult<common::MaaId> {
207 let id = unsafe {
208 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
209 };
210 Ok(id)
211 }
212
213 pub fn post_swipe(
220 &self,
221 x1: i32,
222 y1: i32,
223 x2: i32,
224 y2: i32,
225 duration: i32,
226 ) -> MaaResult<common::MaaId> {
227 let id = unsafe {
228 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
229 };
230 Ok(id)
231 }
232
233 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
238 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
239 Ok(id)
240 }
241
242 #[deprecated(note = "Use post_click_key instead")]
244 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
245 self.post_click_key(keycode)
246 }
247
248 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
253 let c_text = CString::new(text)?;
254 let id =
255 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
256 Ok(id)
257 }
258
259 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
265 let c_cmd = CString::new(cmd)?;
266 let id = unsafe {
267 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
268 };
269 Ok(id)
270 }
271
272 pub fn post_touch_down(
279 &self,
280 contact: i32,
281 x: i32,
282 y: i32,
283 pressure: i32,
284 ) -> MaaResult<common::MaaId> {
285 let id = unsafe {
286 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
287 };
288 Ok(id)
289 }
290
291 pub fn post_touch_move(
298 &self,
299 contact: i32,
300 x: i32,
301 y: i32,
302 pressure: i32,
303 ) -> MaaResult<common::MaaId> {
304 let id = unsafe {
305 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
306 };
307 Ok(id)
308 }
309
310 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
315 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
316 Ok(id)
317 }
318
319 #[inline]
321 pub fn raw(&self) -> *mut sys::MaaController {
322 self.inner.handle.as_ptr()
323 }
324
325 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
331 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
332 Ok(id)
333 }
334
335 pub fn connected(&self) -> bool {
337 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
338 }
339
340 pub fn uuid(&self) -> MaaResult<String> {
342 let buffer = crate::buffer::MaaStringBuffer::new()?;
343 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
344 if ret != 0 {
345 Ok(buffer.to_string())
346 } else {
347 Err(MaaError::FrameworkError(0))
348 }
349 }
350
351 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
353 let mut width: i32 = 0;
354 let mut height: i32 = 0;
355 let ret = unsafe {
356 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
357 };
358 if ret != 0 {
359 Ok((width, height))
360 } else {
361 Err(MaaError::FrameworkError(0))
362 }
363 }
364
365 pub fn post_swipe_v2(
376 &self,
377 x1: i32,
378 y1: i32,
379 x2: i32,
380 y2: i32,
381 duration: i32,
382 contact: i32,
383 pressure: i32,
384 ) -> MaaResult<common::MaaId> {
385 let id = unsafe {
386 sys::MaaControllerPostSwipeV2(
387 self.inner.handle.as_ptr(),
388 x1,
389 y1,
390 x2,
391 y2,
392 duration,
393 contact,
394 pressure,
395 )
396 };
397 Ok(id)
398 }
399
400 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
404 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
405 Ok(id)
406 }
407
408 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
410 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
411 Ok(id)
412 }
413
414 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
421 let c_intent = CString::new(intent)?;
422 let id = unsafe {
423 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
424 };
425 Ok(id)
426 }
427
428 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
433 let c_intent = CString::new(intent)?;
434 let id =
435 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
436 Ok(id)
437 }
438
439 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
447 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
448 Ok(id)
449 }
450
451 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
455 let buffer = crate::buffer::MaaImageBuffer::new()?;
456 let ret =
457 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
458 if ret != 0 {
459 Ok(buffer)
460 } else {
461 Err(MaaError::FrameworkError(0))
462 }
463 }
464
465 pub fn shell_output(&self) -> MaaResult<String> {
469 let buffer = crate::buffer::MaaStringBuffer::new()?;
470 let ret = unsafe {
471 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
472 };
473 if ret != 0 {
474 Ok(buffer.to_string())
475 } else {
476 Err(MaaError::FrameworkError(0))
477 }
478 }
479
480 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
484 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
485 common::MaaStatus(s)
486 }
487
488 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
490 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
491 common::MaaStatus(s)
492 }
493
494 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
498 let mut val = long_side;
499 let ret = unsafe {
500 sys::MaaControllerSetOption(
501 self.inner.handle.as_ptr(),
502 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
503 &mut val as *mut _ as *mut c_void,
504 std::mem::size_of::<i32>() as u64,
505 )
506 };
507 common::check_bool(ret)
508 }
509
510 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
512 let mut val = short_side;
513 let ret = unsafe {
514 sys::MaaControllerSetOption(
515 self.inner.handle.as_ptr(),
516 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
517 &mut val as *mut _ as *mut c_void,
518 std::mem::size_of::<i32>() as u64,
519 )
520 };
521 common::check_bool(ret)
522 }
523
524 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
526 let mut val: u8 = if enable { 1 } else { 0 };
527 let ret = unsafe {
528 sys::MaaControllerSetOption(
529 self.inner.handle.as_ptr(),
530 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
531 &mut val as *mut _ as *mut c_void,
532 std::mem::size_of::<u8>() as u64,
533 )
534 };
535 common::check_bool(ret)
536 }
537
538 #[cfg(feature = "dbg")]
541 pub fn new_dbg(
542 read_path: &str,
543 write_path: &str,
544 dbg_type: sys::MaaDbgControllerType,
545 config: &str,
546 ) -> MaaResult<Self> {
547 let c_read = CString::new(read_path)?;
548 let c_write = CString::new(write_path)?;
549 let c_cfg = CString::new(config)?;
550 let handle = unsafe {
551 sys::MaaDbgControllerCreate(c_read.as_ptr(), c_write.as_ptr(), dbg_type, c_cfg.as_ptr())
552 };
553 Self::from_handle(handle)
554 }
555
556 #[cfg(feature = "win32")]
558 pub fn new_gamepad(
559 hwnd: *mut c_void,
560 gamepad_type: crate::common::GamepadType,
561 screencap_method: crate::common::Win32ScreencapMethod,
562 ) -> MaaResult<Self> {
563 let handle = unsafe {
564 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
565 };
566 Self::from_handle(handle)
567 }
568
569 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
573 where
574 F: Fn(&str, &str) + Send + Sync + 'static,
575 {
576 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
577 let sink_id =
578 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
579 if sink_id != 0 {
580 self.inner
581 .callbacks
582 .lock()
583 .unwrap()
584 .insert(sink_id, cb_arg as usize);
585 Ok(sink_id)
586 } else {
587 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
588 Err(MaaError::FrameworkError(0))
589 }
590 }
591
592 pub fn add_event_sink(
604 &self,
605 sink: Box<dyn crate::event_sink::EventSink>,
606 ) -> MaaResult<sys::MaaSinkId> {
607 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
608 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
609 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
610 if id > 0 {
611 self.inner
612 .event_sinks
613 .lock()
614 .unwrap()
615 .insert(id, arg as usize);
616 Ok(id)
617 } else {
618 unsafe { crate::callback::EventCallback::drop_sink(arg) };
619 Err(MaaError::FrameworkError(0))
620 }
621 }
622
623 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
624 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
625 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
626 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
627 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
628 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
629 }
630 }
631
632 pub fn clear_sinks(&self) {
633 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
634 let mut callbacks = self.inner.callbacks.lock().unwrap();
635 for (_, ptr) in callbacks.drain() {
636 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
637 }
638 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
639 for (_, ptr) in event_sinks.drain() {
640 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
641 }
642 }
643}
644
645impl Drop for ControllerInner {
646 fn drop(&mut self) {
647 unsafe {
648 sys::MaaControllerClearSinks(self.handle.as_ptr());
649 let mut callbacks = self.callbacks.lock().unwrap();
650 for (_, ptr) in callbacks.drain() {
651 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
652 }
653 let mut event_sinks = self.event_sinks.lock().unwrap();
654 for (_, ptr) in event_sinks.drain() {
655 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
656 }
657 if self.owns_handle {
658 sys::MaaControllerDestroy(self.handle.as_ptr());
659 }
660 }
661 }
662}
663
664#[cfg(feature = "adb")]
668pub struct AdbControllerBuilder {
669 adb_path: String,
670 address: String,
671 screencap_methods: sys::MaaAdbScreencapMethod,
672 input_methods: sys::MaaAdbInputMethod,
673 config: String,
674 agent_path: String,
675}
676
677#[cfg(feature = "adb")]
678impl AdbControllerBuilder {
679 pub fn new(adb_path: &str, address: &str) -> Self {
681 Self {
682 adb_path: adb_path.to_string(),
683 address: address.to_string(),
684 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
685 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
686 config: "{}".to_string(),
687 agent_path: "./MaaAgentBinary".to_string(),
688 }
689 }
690
691 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
693 self.screencap_methods = methods;
694 self
695 }
696
697 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
699 self.input_methods = methods;
700 self
701 }
702
703 pub fn config(mut self, config: &str) -> Self {
705 self.config = config.to_string();
706 self
707 }
708
709 pub fn agent_path(mut self, path: &str) -> Self {
711 self.agent_path = path.to_string();
712 self
713 }
714
715 pub fn build(self) -> MaaResult<Controller> {
717 Controller::create_adb(
719 &self.adb_path,
720 &self.address,
721 self.screencap_methods,
722 self.input_methods,
723 &self.config,
724 &self.agent_path,
725 )
726 }
727}
728
729pub struct ControllerRef<'a> {
735 handle: *mut sys::MaaController,
736 _marker: std::marker::PhantomData<&'a ()>,
737}
738
739impl<'a> std::fmt::Debug for ControllerRef<'a> {
740 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
741 f.debug_struct("ControllerRef")
742 .field("handle", &self.handle)
743 .finish()
744 }
745}
746
747impl<'a> ControllerRef<'a> {
748 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
749 if handle.is_null() {
750 None
751 } else {
752 Some(Self {
753 handle,
754 _marker: std::marker::PhantomData,
755 })
756 }
757 }
758
759 pub fn connected(&self) -> bool {
761 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
762 }
763
764 pub fn uuid(&self) -> MaaResult<String> {
766 let buffer = crate::buffer::MaaStringBuffer::new()?;
767 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
768 if ret != 0 {
769 Ok(buffer.to_string())
770 } else {
771 Err(MaaError::FrameworkError(0))
772 }
773 }
774
775 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
777 let mut width: i32 = 0;
778 let mut height: i32 = 0;
779 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
780 if ret != 0 {
781 Ok((width, height))
782 } else {
783 Err(MaaError::FrameworkError(0))
784 }
785 }
786
787 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
789 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
790 common::MaaStatus(s)
791 }
792
793 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
795 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
796 common::MaaStatus(s)
797 }
798
799 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
801 let buffer = crate::buffer::MaaImageBuffer::new()?;
802 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
803 if ret != 0 {
804 Ok(buffer)
805 } else {
806 Err(MaaError::FrameworkError(0))
807 }
808 }
809
810 pub fn raw(&self) -> *mut sys::MaaController {
812 self.handle
813 }
814}