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 = 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}