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