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")]
73 fn resolve_agent_path(agent_path: &str) -> MaaResult<String> {
74 if !agent_path.is_empty() {
75 return Ok(agent_path.to_string());
76 }
77 let cur = std::env::current_dir().
78 map_err(|e| {
79 MaaError::InvalidArgument(
80 format!("agent_path empty and current_dir failed: {}", e)
81 )
82 })?;
83 let s = cur.
84 to_str().
85 ok_or_else(|| {
86 MaaError::InvalidArgument(
87 "agent_path empty and current directory is not valid UTF-8".to_string(),
88 )
89 })?;
90 Ok(s.to_string())
91 }
92
93 #[cfg(feature = "adb")]
94 pub(crate) fn create_adb(
95 adb_path: &str,
96 address: &str,
97 screencap_method: sys::MaaAdbScreencapMethod,
98 input_method: sys::MaaAdbInputMethod,
99 config: &str,
100 agent_path: &str,
101 ) -> MaaResult<Self> {
102 let path = Self::resolve_agent_path(agent_path)?;
103 let c_adb = CString::new(adb_path)?;
104 let c_addr = CString::new(address)?;
105 let c_cfg = CString::new(config)?;
106 let c_agent = CString::new(path.as_str())?;
107
108 let handle = unsafe {
109 sys::MaaAdbControllerCreate(
110 c_adb.as_ptr(),
111 c_addr.as_ptr(),
112 screencap_method,
113 input_method,
114 c_cfg.as_ptr(),
115 c_agent.as_ptr(),
116 )
117 };
118
119 if let Some(ptr) = NonNull::new(handle) {
120 Ok(Self {
121 inner: Arc::new(ControllerInner {
122 handle: ptr,
123 owns_handle: true,
124 callbacks: Mutex::new(HashMap::new()),
125 event_sinks: Mutex::new(HashMap::new()),
126 }),
127 })
128 } else {
129 Err(MaaError::FrameworkError(-1))
130 }
131 }
132
133 #[cfg(feature = "win32")]
135 pub fn new_win32(
136 hwnd: *mut c_void,
137 screencap_method: sys::MaaWin32ScreencapMethod,
138 mouse_method: sys::MaaWin32InputMethod,
139 keyboard_method: sys::MaaWin32InputMethod,
140 ) -> MaaResult<Self> {
141 let handle = unsafe {
142 sys::MaaWin32ControllerCreate(hwnd, screencap_method, mouse_method, keyboard_method)
143 };
144
145 Self::from_handle(handle)
146 }
147
148 pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self> {
151 let c_addr = CString::new(address)?;
152 let c_uuid = CString::new(uuid)?;
153 let handle = unsafe { sys::MaaPlayCoverControllerCreate(c_addr.as_ptr(), c_uuid.as_ptr()) };
154
155 Self::from_handle(handle)
156 }
157
158 #[cfg(feature = "custom")]
160 pub fn new_custom<T: crate::custom_controller::CustomControllerCallback + 'static>(
161 callback: T,
162 ) -> MaaResult<Self> {
163 let boxed: Box<Box<dyn crate::custom_controller::CustomControllerCallback>> =
164 Box::new(Box::new(callback));
165 let cb_ptr = Box::into_raw(boxed) as *mut c_void;
166 let callbacks = crate::custom_controller::get_callbacks();
167 let handle =
168 unsafe { sys::MaaCustomControllerCreate(callbacks as *const _ as *mut _, cb_ptr) };
169
170 NonNull::new(handle)
171 .map(|ptr| Self {
172 inner: Arc::new(ControllerInner {
173 handle: ptr,
174 owns_handle: true,
175 callbacks: Mutex::new(HashMap::new()),
176 event_sinks: Mutex::new(HashMap::new()),
177 }),
178 })
179 .ok_or_else(|| {
180 unsafe {
181 let _ = Box::from_raw(
182 cb_ptr as *mut Box<dyn crate::custom_controller::CustomControllerCallback>,
183 );
184 }
185 MaaError::FrameworkError(-1)
186 })
187 }
188
189 fn from_handle(handle: *mut sys::MaaController) -> MaaResult<Self> {
191 if let Some(ptr) = NonNull::new(handle) {
192 Ok(Self {
193 inner: Arc::new(ControllerInner {
194 handle: ptr,
195 owns_handle: true,
196 callbacks: Mutex::new(HashMap::new()),
197 event_sinks: Mutex::new(HashMap::new()),
198 }),
199 })
200 } else {
201 Err(MaaError::FrameworkError(-1))
202 }
203 }
204
205 pub fn post_click(&self, x: i32, y: i32) -> MaaResult<common::MaaId> {
207 let id = unsafe { sys::MaaControllerPostClick(self.inner.handle.as_ptr(), x, y) };
208 Ok(id)
209 }
210
211 pub fn post_screencap(&self) -> MaaResult<common::MaaId> {
213 let id = unsafe { sys::MaaControllerPostScreencap(self.inner.handle.as_ptr()) };
214 Ok(id)
215 }
216
217 pub fn post_click_v2(
224 &self,
225 x: i32,
226 y: i32,
227 contact: i32,
228 pressure: i32,
229 ) -> MaaResult<common::MaaId> {
230 let id = unsafe {
231 sys::MaaControllerPostClickV2(self.inner.handle.as_ptr(), x, y, contact, pressure)
232 };
233 Ok(id)
234 }
235
236 pub fn post_swipe(
243 &self,
244 x1: i32,
245 y1: i32,
246 x2: i32,
247 y2: i32,
248 duration: i32,
249 ) -> MaaResult<common::MaaId> {
250 let id = unsafe {
251 sys::MaaControllerPostSwipe(self.inner.handle.as_ptr(), x1, y1, x2, y2, duration)
252 };
253 Ok(id)
254 }
255
256 pub fn post_click_key(&self, keycode: i32) -> MaaResult<common::MaaId> {
261 let id = unsafe { sys::MaaControllerPostClickKey(self.inner.handle.as_ptr(), keycode) };
262 Ok(id)
263 }
264
265 #[deprecated(note = "Use post_click_key instead")]
267 pub fn post_press(&self, keycode: i32) -> MaaResult<common::MaaId> {
268 self.post_click_key(keycode)
269 }
270
271 pub fn post_input_text(&self, text: &str) -> MaaResult<common::MaaId> {
276 let c_text = CString::new(text)?;
277 let id =
278 unsafe { sys::MaaControllerPostInputText(self.inner.handle.as_ptr(), c_text.as_ptr()) };
279 Ok(id)
280 }
281
282 pub fn post_shell(&self, cmd: &str, timeout: i64) -> MaaResult<common::MaaId> {
288 let c_cmd = CString::new(cmd)?;
289 let id = unsafe {
290 sys::MaaControllerPostShell(self.inner.handle.as_ptr(), c_cmd.as_ptr(), timeout)
291 };
292 Ok(id)
293 }
294
295 pub fn post_touch_down(
302 &self,
303 contact: i32,
304 x: i32,
305 y: i32,
306 pressure: i32,
307 ) -> MaaResult<common::MaaId> {
308 let id = unsafe {
309 sys::MaaControllerPostTouchDown(self.inner.handle.as_ptr(), contact, x, y, pressure)
310 };
311 Ok(id)
312 }
313
314 pub fn post_touch_move(
321 &self,
322 contact: i32,
323 x: i32,
324 y: i32,
325 pressure: i32,
326 ) -> MaaResult<common::MaaId> {
327 let id = unsafe {
328 sys::MaaControllerPostTouchMove(self.inner.handle.as_ptr(), contact, x, y, pressure)
329 };
330 Ok(id)
331 }
332
333 pub fn post_touch_up(&self, contact: i32) -> MaaResult<common::MaaId> {
338 let id = unsafe { sys::MaaControllerPostTouchUp(self.inner.handle.as_ptr(), contact) };
339 Ok(id)
340 }
341
342 #[inline]
344 pub fn raw(&self) -> *mut sys::MaaController {
345 self.inner.handle.as_ptr()
346 }
347
348 pub fn post_connection(&self) -> MaaResult<common::MaaId> {
354 let id = unsafe { sys::MaaControllerPostConnection(self.inner.handle.as_ptr()) };
355 Ok(id)
356 }
357
358 pub fn connected(&self) -> bool {
360 unsafe { sys::MaaControllerConnected(self.inner.handle.as_ptr()) != 0 }
361 }
362
363 pub fn uuid(&self) -> MaaResult<String> {
365 let buffer = crate::buffer::MaaStringBuffer::new()?;
366 let ret = unsafe { sys::MaaControllerGetUuid(self.inner.handle.as_ptr(), buffer.as_ptr()) };
367 if ret != 0 {
368 Ok(buffer.to_string())
369 } else {
370 Err(MaaError::FrameworkError(0))
371 }
372 }
373
374 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
376 let mut width: i32 = 0;
377 let mut height: i32 = 0;
378 let ret = unsafe {
379 sys::MaaControllerGetResolution(self.inner.handle.as_ptr(), &mut width, &mut height)
380 };
381 if ret != 0 {
382 Ok((width, height))
383 } else {
384 Err(MaaError::FrameworkError(0))
385 }
386 }
387
388 pub fn post_swipe_v2(
399 &self,
400 x1: i32,
401 y1: i32,
402 x2: i32,
403 y2: i32,
404 duration: i32,
405 contact: i32,
406 pressure: i32,
407 ) -> MaaResult<common::MaaId> {
408 let id = unsafe {
409 sys::MaaControllerPostSwipeV2(
410 self.inner.handle.as_ptr(),
411 x1,
412 y1,
413 x2,
414 y2,
415 duration,
416 contact,
417 pressure,
418 )
419 };
420 Ok(id)
421 }
422
423 pub fn post_key_down(&self, keycode: i32) -> MaaResult<common::MaaId> {
427 let id = unsafe { sys::MaaControllerPostKeyDown(self.inner.handle.as_ptr(), keycode) };
428 Ok(id)
429 }
430
431 pub fn post_key_up(&self, keycode: i32) -> MaaResult<common::MaaId> {
433 let id = unsafe { sys::MaaControllerPostKeyUp(self.inner.handle.as_ptr(), keycode) };
434 Ok(id)
435 }
436
437 pub fn post_start_app(&self, intent: &str) -> MaaResult<common::MaaId> {
444 let c_intent = CString::new(intent)?;
445 let id = unsafe {
446 sys::MaaControllerPostStartApp(self.inner.handle.as_ptr(), c_intent.as_ptr())
447 };
448 Ok(id)
449 }
450
451 pub fn post_stop_app(&self, intent: &str) -> MaaResult<common::MaaId> {
456 let c_intent = CString::new(intent)?;
457 let id =
458 unsafe { sys::MaaControllerPostStopApp(self.inner.handle.as_ptr(), c_intent.as_ptr()) };
459 Ok(id)
460 }
461
462 pub fn post_scroll(&self, dx: i32, dy: i32) -> MaaResult<common::MaaId> {
470 let id = unsafe { sys::MaaControllerPostScroll(self.inner.handle.as_ptr(), dx, dy) };
471 Ok(id)
472 }
473
474 pub fn post_inactive(&self) -> MaaResult<common::MaaId> {
481 let id = unsafe { sys::MaaControllerPostInactive(self.inner.handle.as_ptr()) };
482 Ok(id)
483 }
484
485 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
489 let buffer = crate::buffer::MaaImageBuffer::new()?;
490 let ret =
491 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
492 if ret != 0 {
493 Ok(buffer)
494 } else {
495 Err(MaaError::FrameworkError(0))
496 }
497 }
498
499 pub fn shell_output(&self) -> MaaResult<String> {
503 let buffer = crate::buffer::MaaStringBuffer::new()?;
504 let ret = unsafe {
505 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
506 };
507 if ret != 0 {
508 Ok(buffer.to_string())
509 } else {
510 Err(MaaError::FrameworkError(0))
511 }
512 }
513
514 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
518 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
519 common::MaaStatus(s)
520 }
521
522 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
524 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
525 common::MaaStatus(s)
526 }
527
528 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
532 let mut val = long_side;
533 let ret = unsafe {
534 sys::MaaControllerSetOption(
535 self.inner.handle.as_ptr(),
536 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
537 &mut val as *mut _ as *mut c_void,
538 std::mem::size_of::<i32>() as u64,
539 )
540 };
541 common::check_bool(ret)
542 }
543
544 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
546 let mut val = short_side;
547 let ret = unsafe {
548 sys::MaaControllerSetOption(
549 self.inner.handle.as_ptr(),
550 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
551 &mut val as *mut _ as *mut c_void,
552 std::mem::size_of::<i32>() as u64,
553 )
554 };
555 common::check_bool(ret)
556 }
557
558 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
560 let mut val: u8 = if enable { 1 } else { 0 };
561 let ret = unsafe {
562 sys::MaaControllerSetOption(
563 self.inner.handle.as_ptr(),
564 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
565 &mut val as *mut _ as *mut c_void,
566 std::mem::size_of::<u8>() as u64,
567 )
568 };
569 common::check_bool(ret)
570 }
571
572 #[cfg(feature = "dbg")]
575 pub fn new_dbg(
576 read_path: &str,
577 write_path: &str,
578 dbg_type: sys::MaaDbgControllerType,
579 config: &str,
580 ) -> MaaResult<Self> {
581 let c_read = CString::new(read_path)?;
582 let c_write = CString::new(write_path)?;
583 let c_cfg = CString::new(config)?;
584 let handle = unsafe {
585 sys::MaaDbgControllerCreate(c_read.as_ptr(), c_write.as_ptr(), dbg_type, c_cfg.as_ptr())
586 };
587 Self::from_handle(handle)
588 }
589
590 #[cfg(feature = "win32")]
592 pub fn new_gamepad(
593 hwnd: *mut c_void,
594 gamepad_type: crate::common::GamepadType,
595 screencap_method: crate::common::Win32ScreencapMethod,
596 ) -> MaaResult<Self> {
597 let handle = unsafe {
598 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
599 };
600 Self::from_handle(handle)
601 }
602
603 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
607 where
608 F: Fn(&str, &str) + Send + Sync + 'static,
609 {
610 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
611 let sink_id =
612 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
613 if sink_id != 0 {
614 self.inner
615 .callbacks
616 .lock()
617 .unwrap()
618 .insert(sink_id, cb_arg as usize);
619 Ok(sink_id)
620 } else {
621 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
622 Err(MaaError::FrameworkError(0))
623 }
624 }
625
626 pub fn add_event_sink(
638 &self,
639 sink: Box<dyn crate::event_sink::EventSink>,
640 ) -> MaaResult<sys::MaaSinkId> {
641 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
642 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
643 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
644 if id > 0 {
645 self.inner
646 .event_sinks
647 .lock()
648 .unwrap()
649 .insert(id, arg as usize);
650 Ok(id)
651 } else {
652 unsafe { crate::callback::EventCallback::drop_sink(arg) };
653 Err(MaaError::FrameworkError(0))
654 }
655 }
656
657 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
658 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
659 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
660 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
661 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
662 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
663 }
664 }
665
666 pub fn clear_sinks(&self) {
667 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
668 let mut callbacks = self.inner.callbacks.lock().unwrap();
669 for (_, ptr) in callbacks.drain() {
670 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
671 }
672 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
673 for (_, ptr) in event_sinks.drain() {
674 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
675 }
676 }
677}
678
679impl Drop for ControllerInner {
680 fn drop(&mut self) {
681 unsafe {
682 sys::MaaControllerClearSinks(self.handle.as_ptr());
683 let mut callbacks = self.callbacks.lock().unwrap();
684 for (_, ptr) in callbacks.drain() {
685 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
686 }
687 let mut event_sinks = self.event_sinks.lock().unwrap();
688 for (_, ptr) in event_sinks.drain() {
689 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
690 }
691 if self.owns_handle {
692 sys::MaaControllerDestroy(self.handle.as_ptr());
693 }
694 }
695 }
696}
697
698#[cfg(feature = "adb")]
702pub struct AdbControllerBuilder {
703 adb_path: String,
704 address: String,
705 screencap_methods: sys::MaaAdbScreencapMethod,
706 input_methods: sys::MaaAdbInputMethod,
707 config: String,
708 agent_path: String,
709}
710
711#[cfg(feature = "adb")]
712impl AdbControllerBuilder {
713 pub fn new(adb_path: &str, address: &str) -> Self {
715 Self {
716 adb_path: adb_path.to_string(),
717 address: address.to_string(),
718 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
719 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
720 config: "{}".to_string(),
721 agent_path: String::new(),
722 }
723 }
724
725 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
727 self.screencap_methods = methods;
728 self
729 }
730
731 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
733 self.input_methods = methods;
734 self
735 }
736
737 pub fn config(mut self, config: &str) -> Self {
739 self.config = config.to_string();
740 self
741 }
742
743 pub fn agent_path(mut self, path: &str) -> Self {
745 self.agent_path = path.to_string();
746 self
747 }
748
749 pub fn build(self) -> MaaResult<Controller> {
751 Controller::create_adb(
752 &self.adb_path,
753 &self.address,
754 self.screencap_methods,
755 self.input_methods,
756 &self.config,
757 &self.agent_path,
758 )
759 }
760}
761
762pub struct ControllerRef<'a> {
768 handle: *mut sys::MaaController,
769 _marker: std::marker::PhantomData<&'a ()>,
770}
771
772impl<'a> std::fmt::Debug for ControllerRef<'a> {
773 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
774 f.debug_struct("ControllerRef")
775 .field("handle", &self.handle)
776 .finish()
777 }
778}
779
780impl<'a> ControllerRef<'a> {
781 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
782 if handle.is_null() {
783 None
784 } else {
785 Some(Self {
786 handle,
787 _marker: std::marker::PhantomData,
788 })
789 }
790 }
791
792 pub fn connected(&self) -> bool {
794 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
795 }
796
797 pub fn uuid(&self) -> MaaResult<String> {
799 let buffer = crate::buffer::MaaStringBuffer::new()?;
800 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
801 if ret != 0 {
802 Ok(buffer.to_string())
803 } else {
804 Err(MaaError::FrameworkError(0))
805 }
806 }
807
808 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
810 let mut width: i32 = 0;
811 let mut height: i32 = 0;
812 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
813 if ret != 0 {
814 Ok((width, height))
815 } else {
816 Err(MaaError::FrameworkError(0))
817 }
818 }
819
820 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
822 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
823 common::MaaStatus(s)
824 }
825
826 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
828 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
829 common::MaaStatus(s)
830 }
831
832 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
834 let buffer = crate::buffer::MaaImageBuffer::new()?;
835 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
836 if ret != 0 {
837 Ok(buffer)
838 } else {
839 Err(MaaError::FrameworkError(0))
840 }
841 }
842
843 pub fn raw(&self) -> *mut sys::MaaController {
845 self.handle
846 }
847}