Skip to main content

maa_framework/
custom_controller.rs

1//! Custom controller implementation for user-defined device control.
2
3use crate::common::ControllerFeature;
4use crate::sys;
5use std::ffi::CStr;
6use std::os::raw::c_void;
7
8/// Custom controller callback trait for implementing custom device control.
9pub trait CustomControllerCallback: Send + Sync {
10    fn connect(&self) -> bool;
11    fn connected(&self) -> bool {
12        true
13    }
14    fn request_uuid(&self) -> Option<String> {
15        None
16    }
17    /// Get the features supported by this controller.
18    ///
19    /// Returns a combination of `ControllerFeature` flags.
20    fn get_features(&self) -> ControllerFeature {
21        ControllerFeature::empty()
22    }
23    fn start_app(&self, _intent: &str) -> bool {
24        false
25    }
26    fn stop_app(&self, _intent: &str) -> bool {
27        false
28    }
29    /// Returns PNG-encoded screenshot data.
30    fn screencap(&self) -> Option<Vec<u8>> {
31        None
32    }
33    fn click(&self, _x: i32, _y: i32) -> bool {
34        false
35    }
36    fn swipe(&self, _x1: i32, _y1: i32, _x2: i32, _y2: i32, _duration: i32) -> bool {
37        false
38    }
39    fn touch_down(&self, _contact: i32, _x: i32, _y: i32, _pressure: i32) -> bool {
40        false
41    }
42    fn touch_move(&self, _contact: i32, _x: i32, _y: i32, _pressure: i32) -> bool {
43        false
44    }
45    fn touch_up(&self, _contact: i32) -> bool {
46        false
47    }
48    fn click_key(&self, _keycode: i32) -> bool {
49        false
50    }
51    fn input_text(&self, _text: &str) -> bool {
52        false
53    }
54    fn key_down(&self, _keycode: i32) -> bool {
55        false
56    }
57    fn key_up(&self, _keycode: i32) -> bool {
58        false
59    }
60    fn scroll(&self, _dx: i32, _dy: i32) -> bool {
61        false
62    }
63}
64
65type BoxedCallback = Box<dyn CustomControllerCallback>;
66
67// FFI trampolines
68unsafe extern "C" fn connect_trampoline(trans_arg: *mut c_void) -> sys::MaaBool {
69    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
70    if cb.connect() {
71        1
72    } else {
73        0
74    }
75}
76
77unsafe extern "C" fn connected_trampoline(trans_arg: *mut c_void) -> sys::MaaBool {
78    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
79    if cb.connected() {
80        1
81    } else {
82        0
83    }
84}
85
86unsafe extern "C" fn request_uuid_trampoline(
87    trans_arg: *mut c_void,
88    buffer: *mut sys::MaaStringBuffer,
89) -> sys::MaaBool {
90    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
91    if let Some(uuid) = cb.request_uuid() {
92        if let Ok(c_str) = std::ffi::CString::new(uuid) {
93            unsafe { sys::MaaStringBufferSetEx(buffer, c_str.as_ptr(), c_str.as_bytes().len() as u64); }
94            return 1;
95        }
96    }
97    0
98}
99
100unsafe extern "C" fn get_features_trampoline(trans_arg: *mut c_void) -> sys::MaaControllerFeature {
101    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
102    cb.get_features().bits()
103}
104
105unsafe extern "C" fn start_app_trampoline(
106    intent: *const std::os::raw::c_char,
107    trans_arg: *mut c_void,
108) -> sys::MaaBool {
109    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
110    let intent_str = if !intent.is_null() {
111        unsafe { CStr::from_ptr(intent).to_string_lossy() }
112    } else {
113        std::borrow::Cow::Borrowed("")
114    };
115    if cb.start_app(&intent_str) {
116        1
117    } else {
118        0
119    }
120}
121
122unsafe extern "C" fn stop_app_trampoline(
123    intent: *const std::os::raw::c_char,
124    trans_arg: *mut c_void,
125) -> sys::MaaBool {
126    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
127    let intent_str = if !intent.is_null() {
128        unsafe { CStr::from_ptr(intent).to_string_lossy() }
129    } else {
130        std::borrow::Cow::Borrowed("")
131    };
132    if cb.stop_app(&intent_str) {
133        1
134    } else {
135        0
136    }
137}
138
139unsafe extern "C" fn screencap_trampoline(
140    trans_arg: *mut c_void,
141    buffer: *mut sys::MaaImageBuffer,
142) -> sys::MaaBool {
143    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
144    if let Some(data) = cb.screencap() {
145        let ret = unsafe {
146            sys::MaaImageBufferSetEncoded(buffer, data.as_ptr() as *mut u8, data.len() as u64)
147        };
148        return ret;
149    }
150    0
151}
152
153unsafe extern "C" fn click_trampoline(x: i32, y: i32, trans_arg: *mut c_void) -> sys::MaaBool {
154    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
155    if cb.click(x, y) {
156        1
157    } else {
158        0
159    }
160}
161
162unsafe extern "C" fn swipe_trampoline(
163    x1: i32,
164    y1: i32,
165    x2: i32,
166    y2: i32,
167    duration: i32,
168    trans_arg: *mut c_void,
169) -> sys::MaaBool {
170    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
171    if cb.swipe(x1, y1, x2, y2, duration) {
172        1
173    } else {
174        0
175    }
176}
177
178unsafe extern "C" fn touch_down_trampoline(
179    contact: i32,
180    x: i32,
181    y: i32,
182    pressure: i32,
183    trans_arg: *mut c_void,
184) -> sys::MaaBool {
185    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
186    if cb.touch_down(contact, x, y, pressure) {
187        1
188    } else {
189        0
190    }
191}
192
193unsafe extern "C" fn touch_move_trampoline(
194    contact: i32,
195    x: i32,
196    y: i32,
197    pressure: i32,
198    trans_arg: *mut c_void,
199) -> sys::MaaBool {
200    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
201    if cb.touch_move(contact, x, y, pressure) {
202        1
203    } else {
204        0
205    }
206}
207
208unsafe extern "C" fn touch_up_trampoline(contact: i32, trans_arg: *mut c_void) -> sys::MaaBool {
209    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
210    if cb.touch_up(contact) {
211        1
212    } else {
213        0
214    }
215}
216
217unsafe extern "C" fn click_key_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
218    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
219    if cb.click_key(keycode) {
220        1
221    } else {
222        0
223    }
224}
225
226unsafe extern "C" fn input_text_trampoline(
227    text: *const std::os::raw::c_char,
228    trans_arg: *mut c_void,
229) -> sys::MaaBool {
230    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
231    let text_str = if !text.is_null() {
232        unsafe { CStr::from_ptr(text).to_string_lossy() }
233    } else {
234        std::borrow::Cow::Borrowed("")
235    };
236    if cb.input_text(&text_str) {
237        1
238    } else {
239        0
240    }
241}
242
243unsafe extern "C" fn key_down_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
244    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
245    if cb.key_down(keycode) {
246        1
247    } else {
248        0
249    }
250}
251
252unsafe extern "C" fn key_up_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
253    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
254    if cb.key_up(keycode) {
255        1
256    } else {
257        0
258    }
259}
260
261unsafe extern "C" fn scroll_trampoline(dx: i32, dy: i32, trans_arg: *mut c_void) -> sys::MaaBool {
262    let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
263    if cb.scroll(dx, dy) {
264        1
265    } else {
266        0
267    }
268}
269
270pub fn create_custom_controller_callbacks() -> sys::MaaCustomControllerCallbacks {
271    sys::MaaCustomControllerCallbacks {
272        connect: Some(connect_trampoline),
273        connected: Some(connected_trampoline),
274        request_uuid: Some(request_uuid_trampoline),
275        get_features: Some(get_features_trampoline),
276        start_app: Some(start_app_trampoline),
277        stop_app: Some(stop_app_trampoline),
278        screencap: Some(screencap_trampoline),
279        click: Some(click_trampoline),
280        swipe: Some(swipe_trampoline),
281        touch_down: Some(touch_down_trampoline),
282        touch_move: Some(touch_move_trampoline),
283        touch_up: Some(touch_up_trampoline),
284        click_key: Some(click_key_trampoline),
285        input_text: Some(input_text_trampoline),
286        key_down: Some(key_down_trampoline),
287        key_up: Some(key_up_trampoline),
288        scroll: Some(scroll_trampoline),
289    }
290}
291
292static CALLBACKS: std::sync::OnceLock<sys::MaaCustomControllerCallbacks> =
293    std::sync::OnceLock::new();
294
295pub fn get_callbacks() -> &'static sys::MaaCustomControllerCallbacks {
296    CALLBACKS.get_or_init(create_custom_controller_callbacks)
297}