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 = &*(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 = &*(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 = &*(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            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 = &*(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 = &*(trans_arg as *const BoxedCallback);
110    let intent_str = if !intent.is_null() {
111        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 = &*(trans_arg as *const BoxedCallback);
127    let intent_str = if !intent.is_null() {
128        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 = &*(trans_arg as *const BoxedCallback);
144    if let Some(data) = cb.screencap() {
145        let ret =
146            sys::MaaImageBufferSetEncoded(buffer, data.as_ptr() as *mut u8, data.len() as u64);
147        return ret;
148    }
149    0
150}
151
152unsafe extern "C" fn click_trampoline(x: i32, y: i32, trans_arg: *mut c_void) -> sys::MaaBool {
153    let cb = &*(trans_arg as *const BoxedCallback);
154    if cb.click(x, y) {
155        1
156    } else {
157        0
158    }
159}
160
161unsafe extern "C" fn swipe_trampoline(
162    x1: i32,
163    y1: i32,
164    x2: i32,
165    y2: i32,
166    duration: i32,
167    trans_arg: *mut c_void,
168) -> sys::MaaBool {
169    let cb = &*(trans_arg as *const BoxedCallback);
170    if cb.swipe(x1, y1, x2, y2, duration) {
171        1
172    } else {
173        0
174    }
175}
176
177unsafe extern "C" fn touch_down_trampoline(
178    contact: i32,
179    x: i32,
180    y: i32,
181    pressure: i32,
182    trans_arg: *mut c_void,
183) -> sys::MaaBool {
184    let cb = &*(trans_arg as *const BoxedCallback);
185    if cb.touch_down(contact, x, y, pressure) {
186        1
187    } else {
188        0
189    }
190}
191
192unsafe extern "C" fn touch_move_trampoline(
193    contact: i32,
194    x: i32,
195    y: i32,
196    pressure: i32,
197    trans_arg: *mut c_void,
198) -> sys::MaaBool {
199    let cb = &*(trans_arg as *const BoxedCallback);
200    if cb.touch_move(contact, x, y, pressure) {
201        1
202    } else {
203        0
204    }
205}
206
207unsafe extern "C" fn touch_up_trampoline(contact: i32, trans_arg: *mut c_void) -> sys::MaaBool {
208    let cb = &*(trans_arg as *const BoxedCallback);
209    if cb.touch_up(contact) {
210        1
211    } else {
212        0
213    }
214}
215
216unsafe extern "C" fn click_key_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
217    let cb = &*(trans_arg as *const BoxedCallback);
218    if cb.click_key(keycode) {
219        1
220    } else {
221        0
222    }
223}
224
225unsafe extern "C" fn input_text_trampoline(
226    text: *const std::os::raw::c_char,
227    trans_arg: *mut c_void,
228) -> sys::MaaBool {
229    let cb = &*(trans_arg as *const BoxedCallback);
230    let text_str = if !text.is_null() {
231        CStr::from_ptr(text).to_string_lossy()
232    } else {
233        std::borrow::Cow::Borrowed("")
234    };
235    if cb.input_text(&text_str) {
236        1
237    } else {
238        0
239    }
240}
241
242unsafe extern "C" fn key_down_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
243    let cb = &*(trans_arg as *const BoxedCallback);
244    if cb.key_down(keycode) {
245        1
246    } else {
247        0
248    }
249}
250
251unsafe extern "C" fn key_up_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
252    let cb = &*(trans_arg as *const BoxedCallback);
253    if cb.key_up(keycode) {
254        1
255    } else {
256        0
257    }
258}
259
260unsafe extern "C" fn scroll_trampoline(dx: i32, dy: i32, trans_arg: *mut c_void) -> sys::MaaBool {
261    let cb = &*(trans_arg as *const BoxedCallback);
262    if cb.scroll(dx, dy) {
263        1
264    } else {
265        0
266    }
267}
268
269pub fn create_custom_controller_callbacks() -> sys::MaaCustomControllerCallbacks {
270    sys::MaaCustomControllerCallbacks {
271        connect: Some(connect_trampoline),
272        connected: Some(connected_trampoline),
273        request_uuid: Some(request_uuid_trampoline),
274        get_features: Some(get_features_trampoline),
275        start_app: Some(start_app_trampoline),
276        stop_app: Some(stop_app_trampoline),
277        screencap: Some(screencap_trampoline),
278        click: Some(click_trampoline),
279        swipe: Some(swipe_trampoline),
280        touch_down: Some(touch_down_trampoline),
281        touch_move: Some(touch_move_trampoline),
282        touch_up: Some(touch_up_trampoline),
283        click_key: Some(click_key_trampoline),
284        input_text: Some(input_text_trampoline),
285        key_down: Some(key_down_trampoline),
286        key_up: Some(key_up_trampoline),
287        scroll: Some(scroll_trampoline),
288    }
289}
290
291static CALLBACKS: std::sync::OnceLock<sys::MaaCustomControllerCallbacks> =
292    std::sync::OnceLock::new();
293
294pub fn get_callbacks() -> &'static sys::MaaCustomControllerCallbacks {
295    CALLBACKS.get_or_init(create_custom_controller_callbacks)
296}