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}
64
65type BoxedCallback = Box<dyn CustomControllerCallback>;
66
67unsafe 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}