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