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 cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
478 let buffer = crate::buffer::MaaImageBuffer::new()?;
479 let ret =
480 unsafe { sys::MaaControllerCachedImage(self.inner.handle.as_ptr(), buffer.as_ptr()) };
481 if ret != 0 {
482 Ok(buffer)
483 } else {
484 Err(MaaError::FrameworkError(0))
485 }
486 }
487
488 pub fn shell_output(&self) -> MaaResult<String> {
492 let buffer = crate::buffer::MaaStringBuffer::new()?;
493 let ret = unsafe {
494 sys::MaaControllerGetShellOutput(self.inner.handle.as_ptr(), buffer.as_ptr())
495 };
496 if ret != 0 {
497 Ok(buffer.to_string())
498 } else {
499 Err(MaaError::FrameworkError(0))
500 }
501 }
502
503 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
507 let s = unsafe { sys::MaaControllerStatus(self.inner.handle.as_ptr(), ctrl_id) };
508 common::MaaStatus(s)
509 }
510
511 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
513 let s = unsafe { sys::MaaControllerWait(self.inner.handle.as_ptr(), ctrl_id) };
514 common::MaaStatus(s)
515 }
516
517 pub fn set_screenshot_target_long_side(&self, long_side: i32) -> MaaResult<()> {
521 let mut val = long_side;
522 let ret = unsafe {
523 sys::MaaControllerSetOption(
524 self.inner.handle.as_ptr(),
525 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetLongSide as i32,
526 &mut val as *mut _ as *mut c_void,
527 std::mem::size_of::<i32>() as u64,
528 )
529 };
530 common::check_bool(ret)
531 }
532
533 pub fn set_screenshot_target_short_side(&self, short_side: i32) -> MaaResult<()> {
535 let mut val = short_side;
536 let ret = unsafe {
537 sys::MaaControllerSetOption(
538 self.inner.handle.as_ptr(),
539 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotTargetShortSide as i32,
540 &mut val as *mut _ as *mut c_void,
541 std::mem::size_of::<i32>() as u64,
542 )
543 };
544 common::check_bool(ret)
545 }
546
547 pub fn set_screenshot_use_raw_size(&self, enable: bool) -> MaaResult<()> {
549 let mut val: u8 = if enable { 1 } else { 0 };
550 let ret = unsafe {
551 sys::MaaControllerSetOption(
552 self.inner.handle.as_ptr(),
553 sys::MaaCtrlOptionEnum_MaaCtrlOption_ScreenshotUseRawSize as i32,
554 &mut val as *mut _ as *mut c_void,
555 std::mem::size_of::<u8>() as u64,
556 )
557 };
558 common::check_bool(ret)
559 }
560
561 #[cfg(feature = "dbg")]
564 pub fn new_dbg(
565 read_path: &str,
566 write_path: &str,
567 dbg_type: sys::MaaDbgControllerType,
568 config: &str,
569 ) -> MaaResult<Self> {
570 let c_read = CString::new(read_path)?;
571 let c_write = CString::new(write_path)?;
572 let c_cfg = CString::new(config)?;
573 let handle = unsafe {
574 sys::MaaDbgControllerCreate(c_read.as_ptr(), c_write.as_ptr(), dbg_type, c_cfg.as_ptr())
575 };
576 Self::from_handle(handle)
577 }
578
579 #[cfg(feature = "win32")]
581 pub fn new_gamepad(
582 hwnd: *mut c_void,
583 gamepad_type: crate::common::GamepadType,
584 screencap_method: crate::common::Win32ScreencapMethod,
585 ) -> MaaResult<Self> {
586 let handle = unsafe {
587 sys::MaaGamepadControllerCreate(hwnd, gamepad_type as u64, screencap_method.bits())
588 };
589 Self::from_handle(handle)
590 }
591
592 pub fn add_sink<F>(&self, callback: F) -> MaaResult<sys::MaaSinkId>
596 where
597 F: Fn(&str, &str) + Send + Sync + 'static,
598 {
599 let (cb_fn, cb_arg) = crate::callback::EventCallback::new(callback);
600 let sink_id =
601 unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb_fn, cb_arg) };
602 if sink_id != 0 {
603 self.inner
604 .callbacks
605 .lock()
606 .unwrap()
607 .insert(sink_id, cb_arg as usize);
608 Ok(sink_id)
609 } else {
610 unsafe { crate::callback::EventCallback::drop_callback(cb_arg) };
611 Err(MaaError::FrameworkError(0))
612 }
613 }
614
615 pub fn add_event_sink(
627 &self,
628 sink: Box<dyn crate::event_sink::EventSink>,
629 ) -> MaaResult<sys::MaaSinkId> {
630 let handle_id = self.inner.handle.as_ptr() as crate::common::MaaId;
631 let (cb, arg) = crate::callback::EventCallback::new_sink(handle_id, sink);
632 let id = unsafe { sys::MaaControllerAddSink(self.inner.handle.as_ptr(), cb, arg) };
633 if id > 0 {
634 self.inner
635 .event_sinks
636 .lock()
637 .unwrap()
638 .insert(id, arg as usize);
639 Ok(id)
640 } else {
641 unsafe { crate::callback::EventCallback::drop_sink(arg) };
642 Err(MaaError::FrameworkError(0))
643 }
644 }
645
646 pub fn remove_sink(&self, sink_id: sys::MaaSinkId) {
647 unsafe { sys::MaaControllerRemoveSink(self.inner.handle.as_ptr(), sink_id) };
648 if let Some(ptr) = self.inner.callbacks.lock().unwrap().remove(&sink_id) {
649 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
650 } else if let Some(ptr) = self.inner.event_sinks.lock().unwrap().remove(&sink_id) {
651 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
652 }
653 }
654
655 pub fn clear_sinks(&self) {
656 unsafe { sys::MaaControllerClearSinks(self.inner.handle.as_ptr()) };
657 let mut callbacks = self.inner.callbacks.lock().unwrap();
658 for (_, ptr) in callbacks.drain() {
659 unsafe { crate::callback::EventCallback::drop_callback(ptr as *mut c_void) };
660 }
661 let mut event_sinks = self.inner.event_sinks.lock().unwrap();
662 for (_, ptr) in event_sinks.drain() {
663 unsafe { crate::callback::EventCallback::drop_sink(ptr as *mut c_void) };
664 }
665 }
666}
667
668impl Drop for ControllerInner {
669 fn drop(&mut self) {
670 unsafe {
671 sys::MaaControllerClearSinks(self.handle.as_ptr());
672 let mut callbacks = self.callbacks.lock().unwrap();
673 for (_, ptr) in callbacks.drain() {
674 crate::callback::EventCallback::drop_callback(ptr as *mut c_void);
675 }
676 let mut event_sinks = self.event_sinks.lock().unwrap();
677 for (_, ptr) in event_sinks.drain() {
678 crate::callback::EventCallback::drop_sink(ptr as *mut c_void);
679 }
680 if self.owns_handle {
681 sys::MaaControllerDestroy(self.handle.as_ptr());
682 }
683 }
684 }
685}
686
687#[cfg(feature = "adb")]
691pub struct AdbControllerBuilder {
692 adb_path: String,
693 address: String,
694 screencap_methods: sys::MaaAdbScreencapMethod,
695 input_methods: sys::MaaAdbInputMethod,
696 config: String,
697 agent_path: String,
698}
699
700#[cfg(feature = "adb")]
701impl AdbControllerBuilder {
702 pub fn new(adb_path: &str, address: &str) -> Self {
704 Self {
705 adb_path: adb_path.to_string(),
706 address: address.to_string(),
707 screencap_methods: sys::MaaAdbScreencapMethod_Default as sys::MaaAdbScreencapMethod,
708 input_methods: sys::MaaAdbInputMethod_Default as sys::MaaAdbInputMethod,
709 config: "{}".to_string(),
710 agent_path: String::new(),
711 }
712 }
713
714 pub fn screencap_methods(mut self, methods: sys::MaaAdbScreencapMethod) -> Self {
716 self.screencap_methods = methods;
717 self
718 }
719
720 pub fn input_methods(mut self, methods: sys::MaaAdbInputMethod) -> Self {
722 self.input_methods = methods;
723 self
724 }
725
726 pub fn config(mut self, config: &str) -> Self {
728 self.config = config.to_string();
729 self
730 }
731
732 pub fn agent_path(mut self, path: &str) -> Self {
734 self.agent_path = path.to_string();
735 self
736 }
737
738 pub fn build(self) -> MaaResult<Controller> {
740 Controller::create_adb(
741 &self.adb_path,
742 &self.address,
743 self.screencap_methods,
744 self.input_methods,
745 &self.config,
746 &self.agent_path,
747 )
748 }
749}
750
751pub struct ControllerRef<'a> {
757 handle: *mut sys::MaaController,
758 _marker: std::marker::PhantomData<&'a ()>,
759}
760
761impl<'a> std::fmt::Debug for ControllerRef<'a> {
762 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
763 f.debug_struct("ControllerRef")
764 .field("handle", &self.handle)
765 .finish()
766 }
767}
768
769impl<'a> ControllerRef<'a> {
770 pub(crate) fn from_ptr(handle: *mut sys::MaaController) -> Option<Self> {
771 if handle.is_null() {
772 None
773 } else {
774 Some(Self {
775 handle,
776 _marker: std::marker::PhantomData,
777 })
778 }
779 }
780
781 pub fn connected(&self) -> bool {
783 unsafe { sys::MaaControllerConnected(self.handle) != 0 }
784 }
785
786 pub fn uuid(&self) -> MaaResult<String> {
788 let buffer = crate::buffer::MaaStringBuffer::new()?;
789 let ret = unsafe { sys::MaaControllerGetUuid(self.handle, buffer.as_ptr()) };
790 if ret != 0 {
791 Ok(buffer.to_string())
792 } else {
793 Err(MaaError::FrameworkError(0))
794 }
795 }
796
797 pub fn resolution(&self) -> MaaResult<(i32, i32)> {
799 let mut width: i32 = 0;
800 let mut height: i32 = 0;
801 let ret = unsafe { sys::MaaControllerGetResolution(self.handle, &mut width, &mut height) };
802 if ret != 0 {
803 Ok((width, height))
804 } else {
805 Err(MaaError::FrameworkError(0))
806 }
807 }
808
809 pub fn status(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
811 let s = unsafe { sys::MaaControllerStatus(self.handle, ctrl_id) };
812 common::MaaStatus(s)
813 }
814
815 pub fn wait(&self, ctrl_id: common::MaaId) -> common::MaaStatus {
817 let s = unsafe { sys::MaaControllerWait(self.handle, ctrl_id) };
818 common::MaaStatus(s)
819 }
820
821 pub fn cached_image(&self) -> MaaResult<crate::buffer::MaaImageBuffer> {
823 let buffer = crate::buffer::MaaImageBuffer::new()?;
824 let ret = unsafe { sys::MaaControllerCachedImage(self.handle, buffer.as_ptr()) };
825 if ret != 0 {
826 Ok(buffer)
827 } else {
828 Err(MaaError::FrameworkError(0))
829 }
830 }
831
832 pub fn raw(&self) -> *mut sys::MaaController {
834 self.handle
835 }
836}