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: Option<&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: Option<&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 = agent_path.map(CString::new).transpose()?;
84 let agent_ptr = c_agent
85 .as_ref()
86 .map(|c| c.as_ptr())
87 .unwrap_or(std::ptr::null());
88
89 let handle = unsafe {
90 sys::MaaAdbControllerCreate(
91 c_adb.as_ptr(),
92 c_addr.as_ptr(),
93 screencap_method,
94 input_method,
95 c_cfg.as_ptr(),
96 agent_ptr,
97 )
98 };
99
100 if let Some(ptr) = NonNull::new(handle) {
101 Ok(Self {
102 inner: Arc::new(ControllerInner {
103 handle: ptr,
104 owns_handle: true,
105 callbacks: Mutex::new(HashMap::new()),
106 event_sinks: Mutex::new(HashMap::new()),
107 }),
108 })
109 } else {
110 Err(MaaError::FrameworkError(-1))
111 }
112 }
113
114 #[cfg(feature = "win32")]
116 pub fn new_win32(
117 hwnd: *mut c_void,
118 screencap_method: sys::MaaWin32ScreencapMethod,
119 mouse_method: sys::MaaWin32InputMethod,
120 keyboard_method: sys::MaaWin32InputMethod,
121 ) -> MaaResult<Self> {
122 let handle = unsafe {
123 sys::MaaWin32ControllerCreate(hwnd, screencap_method, mouse_method, keyboard_method)
124 };
125
126 Self::from_handle(handle)
127 }
128
129 pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self> {
132 let c_addr = CString::new(address)?;
133 let c_uuid = CString::new(uuid)?;
134 let handle = unsafe { sys::MaaPlayCoverControllerCreate(c_addr.as_ptr(), c_uuid.as_ptr()) };
135
136 Self::from_handle(handle)
137 }
138
139 #[cfg(feature = "custom")]
141 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
142 callback: T,
143 ) -> MaaResult<Self> {
144 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
145 Box::new(Box::new(callback));
146 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
147 let callbacks = crate::custom_controller::get_callbacks();
148 let handle =
149 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
150
151 NonNull::new(handle)
152 .map(|ptr| Self {
153 inner: Arc::new(ControllerInner {
154 handle: ptr,
155 owns_handle: true,
156 callbacks: Mutex::new(HashMap::new()),
157 event_sinks: Mutex::new(HashMap::new()),
158 }),
159 })
160 .ok_or_else(|| {
161 unsafe {
162 let _ = Box::from_raw(
163 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
164 );
165 }
166 MaaError::FrameworkError(-1)
167 })
168 }
169
170 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
172 if let Some(ptr) = NonNull::new(handle) {
173 Ok(Self {
174 inner: Arc::new(ControllerInner {
175 handle: ptr,
176 owns_handle: true,
177 callbacks: Mutex::new(HashMap::new()),
178 event_sinks: Mutex::new(HashMap::new()),
179 }),
180 })
181 } else {
182 Err(MaaError::FrameworkError(-1))
183 }
184 }
185
186 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
188 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
189 Ok(id)
190 }
191
192 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
194 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
195 Ok(id)
196 }
197
198 pub fn post_click_v2(
205 &self,
206 x: i32,
207 y: i32,
208 contact: i32,
209 pressure: i32,
210 ) -> MaaResult<common::MaaId> {
211 let id = unsafe {
212 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
213 };
214 Ok(id)
215 }
216
217 pub fn post_swipe(
224 &self,
225 x1: i32,
226 y1: i32,
227 x2: i32,
228 y2: i32,
229 duration: i32,
230 ) -> MaaResult<common::MaaId> {
231 let id = unsafe {
232 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
233 };
234 Ok(id)
235 }
236
237 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
242 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
243 Ok(id)
244 }
245
246 #[deprecated(note = "Use post_click_key instead")]
248 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
249 self.post_click_key(keycode)
250 }
251
252 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
257 let c_text = CString::new(text)?;
258 let id =
259 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
260 Ok(id)
261 }
262
263 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
269 let c_cmd = CString::new(cmd)?;
270 let id = unsafe {
271 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
272 };
273 Ok(id)
274 }
275
276 pub fn post_touch_down(
283 &self,
284 contact: i32,
285 x: i32,
286 y: i32,
287 pressure: i32,
288 ) -> MaaResult<common::MaaId> {
289 let id = unsafe {
290 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
291 };
292 Ok(id)
293 }
294
295 pub fn post_touch_move(
302 &self,
303 contact: i32,
304 x: i32,
305 y: i32,
306 pressure: i32,
307 ) -> MaaResult<common::MaaId> {
308 let id = unsafe {
309 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
310 };
311 Ok(id)
312 }
313
314 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
319 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
320 Ok(id)
321 }
322
323 #[inline]
325 pub fn raw(&self) -> *mut sys::MaaController {
326 self.inner.handle.as_ptr()
327 }
328
329 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
335 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
336 Ok(id)
337 }
338
339 pub fn connected(&self) -> bool {
341 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
342 }
343
344 pub fn uuid(&self) -> MaaResult<String> {
346 let buffer = crate::buffer::MaaStringBuffer::new()?;
347 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
348 if ret != 0 {
349 Ok(buffer.to_string())
350 } else {
351 Err(MaaError::FrameworkError(0))
352 }
353 }
354
355 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
357 let mut width: i32 = 0;
358 let mut height: i32 = 0;
359 let ret = unsafe {
360 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
361 };
362 if ret != 0 {
363 Ok((width, height))
364 } else {
365 Err(MaaError::FrameworkError(0))
366 }
367 }
368
369 pub fn post_swipe_v2(
380 &self,
381 x1: i32,
382 y1: i32,
383 x2: i32,
384 y2: i32,
385 duration: i32,
386 contact: i32,
387 pressure: i32,
388 ) -> MaaResult<common::MaaId> {
389 let id = unsafe {
390 sys::MaaControllerPostSwipeV2(
391 self.inner.handle.as_ptr(),
392 x1,
393 y1,
394 x2,
395 y2,
396 duration,
397 contact,
398 pressure,
399 )
400 };
401 Ok(id)
402 }
403
404 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
408 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
409 Ok(id)
410 }
411
412 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
414 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
415 Ok(id)
416 }
417
418 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
425 let c_intent = CString::new(intent)?;
426 let id = unsafe {
427 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
428 };
429 Ok(id)
430 }
431
432 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
437 let c_intent = CString::new(intent)?;
438 let id =
439 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
440 Ok(id)
441 }
442
443 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
451 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
452 Ok(id)
453 }
454
455 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
459 let buffer = crate::buffer::MaaImageBuffer::new()?;
460 let ret =
461 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
462 if ret != 0 {
463 Ok(buffer)
464 } else {
465 Err(MaaError::FrameworkError(0))
466 }
467 }
468
469 pub fn shell_output(&self) -> MaaResult<String> {
473 let buffer = crate::buffer::MaaStringBuffer::new()?;
474 let ret = unsafe {
475 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
476 };
477 if ret != 0 {
478 Ok(buffer.to_string())
479 } else {
480 Err(MaaError::FrameworkError(0))
481 }
482 }
483
484 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
488 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
489 common::MaaStatus(s)
490 }
491
492 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
494 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
495 common::MaaStatus(s)
496 }
497
498 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
502 let mut val = long_side;
503 let ret = unsafe {
504 sys::MaaControllerSetOption(
505 self.inner.handle.as_ptr(),
506 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
507 &mut val as *mut _ as *mut c_void,
508 std::mem::size_of::<i32>() as u64,
509 )
510 };
511 common::check_bool(ret)
512 }
513
514 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
516 let mut val = short_side;
517 let ret = unsafe {
518 sys::MaaControllerSetOption(
519 self.inner.handle.as_ptr(),
520 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
521 &mut val as *mut _ as *mut c_void,
522 std::mem::size_of::<i32>() as u64,
523 )
524 };
525 common::check_bool(ret)
526 }
527
528 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
530 let mut val: u8 = if enable { 1 } else { 0 };
531 let ret = unsafe {
532 sys::MaaControllerSetOption(
533 self.inner.handle.as_ptr(),
534 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
535 &mut val as *mut _ as *mut c_void,
536 std::mem::size_of::<u8>() as u64,
537 )
538 };
539 common::check_bool(ret)
540 }
541
542 #[cfg(feature = "dbg")]
545 pub fn new_dbg(
546 read_path: &str,
547 write_path: &str,
548 dbg_type: sys::MaaDbgControllerType,
549 config: &str,
550 ) -> MaaResult<Self> {
551 let c_read = CString::new(read_path)?;
552 let c_write = CString::new(write_path)?;
553 let c_cfg = CString::new(config)?;
554 let handle = unsafe {
555 sys::MaaDbgControllerCreate(c_read.as_ptr(), c_write.as_ptr(), dbg_type, c_cfg.as_ptr())
556 };
557 Self::from_handle(handle)
558 }
559
560 #[cfg(feature = "win32")]
562 pub fn new_gamepad(
563 hwnd: *mut c_void,
564 gamepad_type: crate::common::GamepadType,
565 screencap_method: crate::common::Win32ScreencapMethod,
566 ) -> MaaResult<Self> {
567 let handle = unsafe {
568 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
569 };
570 Self::from_handle(handle)
571 }
572
573 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
577 where
578 F: Fn(&str, &str) + Send + Sync + 'static,
579 {
580 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
581 let sink_id =
582 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
583 if sink_id != 0 {
584 self.inner
585 .callbacks
586 .lock()
587 .unwrap()
588 .insert(sink_id, cb_arg as usize);
589 Ok(sink_id)
590 } else {
591 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
592 Err(MaaError::FrameworkError(0))
593 }
594 }
595
596 pub fn add_event_sink(
608 &self,
609 sink: Box<dyn crate::event_sink::EventSink>,
610 ) -> MaaResult<sys::MaaSinkId> {
611 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
612 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
613 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
614 if id > 0 {
615 self.inner
616 .event_sinks
617 .lock()
618 .unwrap()
619 .insert(id, arg as usize);
620 Ok(id)
621 } else {
622 unsafe { crate::callback::EventCallback::drop_sink(arg) };
623 Err(MaaError::FrameworkError(0))
624 }
625 }
626
627 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
628 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
629 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
630 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
631 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
632 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
633 }
634 }
635
636 pub fn clear_sinks(&self) {
637 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
638 let mut callbacks = self.inner.callbacks.lock().unwrap();
639 for (_, ptr) in callbacks.drain() {
640 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
641 }
642 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
643 for (_, ptr) in event_sinks.drain() {
644 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
645 }
646 }
647}
648
649impl Drop for ControllerInner {
650 fn drop(&mut self) {
651 unsafe {
652 sys::MaaControllerClearSinks(self.handle.as_ptr());
653 let mut callbacks = self.callbacks.lock().unwrap();
654 for (_, ptr) in callbacks.drain() {
655 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
656 }
657 let mut event_sinks = self.event_sinks.lock().unwrap();
658 for (_, ptr) in event_sinks.drain() {
659 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
660 }
661 if self.owns_handle {
662 sys::MaaControllerDestroy(self.handle.as_ptr());
663 }
664 }
665 }
666}
667
668#[cfg(feature = "adb")]
672pub struct AdbControllerBuilder {
673 adb_path: String,
674 address: String,
675 screencap_methods: sys::MaaAdbScreencapMethod,
676 input_methods: sys::MaaAdbInputMethod,
677 config: String,
678 agent_path: Option<String>,
679}
680
681#[cfg(feature = "adb")]
682impl AdbControllerBuilder {
683 pub fn new(adb_path: &str, address: &str) -> Self {
685 Self {
686 adb_path: adb_path.to_string(),
687 address: address.to_string(),
688 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
689 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
690 config: "{}".to_string(),
691 agent_path: None,
692 }
693 }
694
695 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
697 self.screencap_methods = methods;
698 self
699 }
700
701 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
703 self.input_methods = methods;
704 self
705 }
706
707 pub fn config(mut self, config: &str) -> Self {
709 self.config = config.to_string();
710 self
711 }
712
713 pub fn agent_path(mut self, path: &str) -> Self {
715 self.agent_path = Some(path.to_string());
716 self
717 }
718
719 pub fn build(self) -> MaaResult<Controller> {
721 Controller::create_adb(
723 &self.adb_path,
724 &self.address,
725 self.screencap_methods,
726 self.input_methods,
727 &self.config,
728 self.agent_path.as_deref(),
729 )
730 }
731}
732
733pub struct ControllerRef<'a> {
739 handle: *mut sys::MaaController,
740 _marker: std::marker::PhantomData<&'a ()>,
741}
742
743impl<'a> std::fmt::Debug for ControllerRef<'a> {
744 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
745 f.debug_struct("ControllerRef")
746 .field("handle", &self.handle)
747 .finish()
748 }
749}
750
751impl<'a> ControllerRef<'a> {
752 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
753 if handle.is_null() {
754 None
755 } else {
756 Some(Self {
757 handle,
758 _marker: std::marker::PhantomData,
759 })
760 }
761 }
762
763 pub fn connected(&self) -> bool {
765 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
766 }
767
768 pub fn uuid(&self) -> MaaResult<String> {
770 let buffer = crate::buffer::MaaStringBuffer::new()?;
771 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
772 if ret != 0 {
773 Ok(buffer.to_string())
774 } else {
775 Err(MaaError::FrameworkError(0))
776 }
777 }
778
779 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
781 let mut width: i32 = 0;
782 let mut height: i32 = 0;
783 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
784 if ret != 0 {
785 Ok((width, height))
786 } else {
787 Err(MaaError::FrameworkError(0))
788 }
789 }
790
791 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
793 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
794 common::MaaStatus(s)
795 }
796
797 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
799 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
800 common::MaaStatus(s)
801 }
802
803 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
805 let buffer = crate::buffer::MaaImageBuffer::new()?;
806 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
807 if ret != 0 {
808 Ok(buffer)
809 } else {
810 Err(MaaError::FrameworkError(0))
811 }
812 }
813
814 pub fn raw(&self) -> *mut sys::MaaController {
816 self.handle
817 }
818}