1use crate::common::ControllerFeature;
4use crate::sys;
5use std::ffi::CStr;
6use std::os::raw::c_void;
7
8pub 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 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 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 fn inactive(&self) -> bool {
68 true
69 }
70}
71
72type BoxedCallback = Box<dyn CustomControllerCallback>;
73
74unsafe extern "C" fn connect_trampoline(trans_arg: *mut c_void) -> sys::MaaBool {
76 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
77 if cb.connect() {
78 1
79 } else {
80 0
81 }
82}
83
84unsafe extern "C" fn connected_trampoline(trans_arg: *mut c_void) -> sys::MaaBool {
85 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
86 if cb.connected() {
87 1
88 } else {
89 0
90 }
91}
92
93unsafe extern "C" fn request_uuid_trampoline(
94 trans_arg: *mut c_void,
95 buffer: *mut sys::MaaStringBuffer,
96) -> sys::MaaBool {
97 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
98 if let Some(uuid) = cb.request_uuid() {
99 if let Ok(c_str) = std::ffi::CString::new(uuid) {
100 unsafe { sys::MaaStringBufferSetEx(buffer, c_str.as_ptr(), c_str.as_bytes().len() as u64); }
101 return 1;
102 }
103 }
104 0
105}
106
107unsafe extern "C" fn get_features_trampoline(trans_arg: *mut c_void) -> sys::MaaControllerFeature {
108 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
109 cb.get_features().bits()
110}
111
112unsafe extern "C" fn start_app_trampoline(
113 intent: *const std::os::raw::c_char,
114 trans_arg: *mut c_void,
115) -> sys::MaaBool {
116 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
117 let intent_str = if !intent.is_null() {
118 unsafe { CStr::from_ptr(intent).to_string_lossy() }
119 } else {
120 std::borrow::Cow::Borrowed("")
121 };
122 if cb.start_app(&intent_str) {
123 1
124 } else {
125 0
126 }
127}
128
129unsafe extern "C" fn stop_app_trampoline(
130 intent: *const std::os::raw::c_char,
131 trans_arg: *mut c_void,
132) -> sys::MaaBool {
133 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
134 let intent_str = if !intent.is_null() {
135 unsafe { CStr::from_ptr(intent).to_string_lossy() }
136 } else {
137 std::borrow::Cow::Borrowed("")
138 };
139 if cb.stop_app(&intent_str) {
140 1
141 } else {
142 0
143 }
144}
145
146unsafe extern "C" fn screencap_trampoline(
147 trans_arg: *mut c_void,
148 buffer: *mut sys::MaaImageBuffer,
149) -> sys::MaaBool {
150 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
151 if let Some(data) = cb.screencap() {
152 let ret = unsafe {
153 sys::MaaImageBufferSetEncoded(buffer, data.as_ptr() as *mut u8, data.len() as u64)
154 };
155 return ret;
156 }
157 0
158}
159
160unsafe extern "C" fn click_trampoline(x: i32, y: i32, trans_arg: *mut c_void) -> sys::MaaBool {
161 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
162 if cb.click(x, y) {
163 1
164 } else {
165 0
166 }
167}
168
169unsafe extern "C" fn swipe_trampoline(
170 x1: i32,
171 y1: i32,
172 x2: i32,
173 y2: i32,
174 duration: i32,
175 trans_arg: *mut c_void,
176) -> sys::MaaBool {
177 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
178 if cb.swipe(x1, y1, x2, y2, duration) {
179 1
180 } else {
181 0
182 }
183}
184
185unsafe extern "C" fn touch_down_trampoline(
186 contact: i32,
187 x: i32,
188 y: i32,
189 pressure: i32,
190 trans_arg: *mut c_void,
191) -> sys::MaaBool {
192 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
193 if cb.touch_down(contact, x, y, pressure) {
194 1
195 } else {
196 0
197 }
198}
199
200unsafe extern "C" fn touch_move_trampoline(
201 contact: i32,
202 x: i32,
203 y: i32,
204 pressure: i32,
205 trans_arg: *mut c_void,
206) -> sys::MaaBool {
207 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
208 if cb.touch_move(contact, x, y, pressure) {
209 1
210 } else {
211 0
212 }
213}
214
215unsafe extern "C" fn touch_up_trampoline(contact: i32, trans_arg: *mut c_void) -> sys::MaaBool {
216 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
217 if cb.touch_up(contact) {
218 1
219 } else {
220 0
221 }
222}
223
224unsafe extern "C" fn click_key_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
225 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
226 if cb.click_key(keycode) {
227 1
228 } else {
229 0
230 }
231}
232
233unsafe extern "C" fn input_text_trampoline(
234 text: *const std::os::raw::c_char,
235 trans_arg: *mut c_void,
236) -> sys::MaaBool {
237 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
238 let text_str = if !text.is_null() {
239 unsafe { CStr::from_ptr(text).to_string_lossy() }
240 } else {
241 std::borrow::Cow::Borrowed("")
242 };
243 if cb.input_text(&text_str) {
244 1
245 } else {
246 0
247 }
248}
249
250unsafe extern "C" fn key_down_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
251 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
252 if cb.key_down(keycode) {
253 1
254 } else {
255 0
256 }
257}
258
259unsafe extern "C" fn key_up_trampoline(keycode: i32, trans_arg: *mut c_void) -> sys::MaaBool {
260 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
261 if cb.key_up(keycode) {
262 1
263 } else {
264 0
265 }
266}
267
268unsafe extern "C" fn scroll_trampoline(dx: i32, dy: i32, trans_arg: *mut c_void) -> sys::MaaBool {
269 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
270 if cb.scroll(dx, dy) {
271 1
272 } else {
273 0
274 }
275}
276
277unsafe extern "C" fn inactive_trampoline(trans_arg: *mut c_void) -> sys::MaaBool {
278 let cb = unsafe { &*(trans_arg as *const BoxedCallback) };
279 if cb.inactive() {
280 1
281 } else {
282 0
283 }
284}
285
286pub fn create_custom_controller_callbacks() -> sys::MaaCustomControllerCallbacks {
287 sys::MaaCustomControllerCallbacks {
288 connect: Some(connect_trampoline),
289 connected: Some(connected_trampoline),
290 request_uuid: Some(request_uuid_trampoline),
291 get_features: Some(get_features_trampoline),
292 start_app: Some(start_app_trampoline),
293 stop_app: Some(stop_app_trampoline),
294 screencap: Some(screencap_trampoline),
295 click: Some(click_trampoline),
296 swipe: Some(swipe_trampoline),
297 touch_down: Some(touch_down_trampoline),
298 touch_move: Some(touch_move_trampoline),
299 touch_up: Some(touch_up_trampoline),
300 click_key: Some(click_key_trampoline),
301 input_text: Some(input_text_trampoline),
302 key_down: Some(key_down_trampoline),
303 key_up: Some(key_up_trampoline),
304 scroll: Some(scroll_trampoline),
305 inactive: Some(inactive_trampoline),
306 }
307}
308
309static CALLBACKS: std::sync::OnceLock<sys::MaaCustomControllerCallbacks> =
310 std::sync::OnceLock::new();
311
312pub fn get_callbacks() -> &'static sys::MaaCustomControllerCallbacks {
313 CALLBACKS.get_or_init(create_custom_controller_callbacks)
314}