1use super::*;
8use super::closures::*;
9use crate::abi;
10use crate::resolve;
11use core::ffi::c_void;
12use crate::mangle::{self, swift_type};
13
14pub fn on_drag_session_updated(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
16 unsafe {
17 opaque_modifier(
18 resolve::on_drag_session_updated_fn(),
19 resolve::on_drag_session_updated_oret(),
20 view,
21 &ignoring_value_words(action),
22 [0.0; 4],
23 )
24 }
25}
26
27pub fn on_drop_session_updated(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
29 unsafe {
30 opaque_modifier(
31 resolve::on_drop_session_updated_fn(),
32 resolve::on_drop_session_updated_oret(),
33 view,
34 &ignoring_value_words(action),
35 [0.0; 4],
36 )
37 }
38}
39
40pub(crate) unsafe fn content_types(kinds: &[ContentType]) -> u64 {
42 unsafe {
43 let meta = crate::type_by_name!(swift_type!(UniformTypeIdentifiers.UTType).as_bytes());
44 let stride = abi::type_bytes(meta);
45 let mut bytes = vec![0u8; stride * kinds.len()];
46 for (i, k) in kinds.iter().enumerate() {
47 let v = resilient_static(meta, k.getter());
50 bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
51 }
52 abi::swift_array(meta, kinds.len(), &bytes)
53 }
54}
55
56#[derive(Clone, Copy, Debug)]
62pub struct ItemProvider(pub(crate) *mut c_void);
63
64impl ItemProvider {
65 pub fn as_ptr(&self) -> *mut c_void {
67 self.0
68 }
69
70 pub fn type_identifiers(&self) -> Vec<String> {
72 unsafe {
73 type Send0 = unsafe extern "C" fn(*mut c_void, *const c_void) -> *mut c_void;
74 let send: Send0 = core::mem::transmute(crate::objc::msg_send_addr());
75 let array = send(self.0, crate::objc::sel(c"registeredTypeIdentifiers"));
76 if array.is_null() {
77 return Vec::new();
78 }
79 type Count = unsafe extern "C" fn(*mut c_void, *const c_void) -> usize;
80 let count: Count = core::mem::transmute(crate::objc::msg_send_addr());
81 let n = count(array, crate::objc::sel(c"count"));
82 type At = unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void;
83 let at: At = core::mem::transmute(crate::objc::msg_send_addr());
84 type Utf8 = unsafe extern "C" fn(*mut c_void, *const c_void) -> *const i8;
85 let utf8: Utf8 = core::mem::transmute(crate::objc::msg_send_addr());
86 (0..n)
87 .map(|i| {
88 let s = at(array, crate::objc::sel(c"objectAtIndex:"), i);
89 let p = utf8(s, crate::objc::sel(c"UTF8String"));
90 if p.is_null() {
91 String::new()
92 } else {
93 core::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
94 }
95 })
96 .collect()
97 }
98 }
99
100 pub fn load_plain_text(&self, then: impl FnOnce(Option<String>) + 'static) {
126 unsafe {
127 let boxed: Box<dyn FnOnce(Option<String>)> = Box::new(then);
128 let capture = Box::into_raw(Box::new(boxed)) as *mut c_void;
129 let block = Box::leak(Box::new(crate::objc::CapturingBlock::new(
132 loaded_item as *const c_void,
133 capture,
134 ))) as *const crate::objc::CapturingBlock;
135 let kind = crate::objc::nsstring("public.plain-text");
136 type Load = unsafe extern "C" fn(
137 *mut c_void, *const c_void, *mut c_void, *mut c_void,
138 *const crate::objc::CapturingBlock,
139 ) -> *mut c_void;
140 let load: Load = core::mem::transmute(crate::objc::msg_send_addr());
141 load(
142 self.0,
143 crate::objc::sel(c"loadItemForTypeIdentifier:options:completionHandler:"),
144 kind,
145 core::ptr::null_mut(),
146 block,
147 );
148 }
149 }
150
151 pub fn plain_text(text: &str) -> Self {
153 unsafe {
154 let cls = crate::objc::class_named(c"NSItemProvider");
155 type Alloc = unsafe extern "C" fn(*mut c_void, *const c_void) -> *mut c_void;
156 let alloc: Alloc = core::mem::transmute(crate::objc::msg_send_addr());
157 let obj = alloc(cls, crate::objc::sel(c"alloc"));
158 let ns_string = crate::objc::class_named(c"NSString");
159 type With = unsafe extern "C" fn(*mut c_void, *const c_void, *const i8) -> *mut c_void;
160 let with: With = core::mem::transmute(crate::objc::msg_send_addr());
161 let c = std::ffi::CString::new(text).expect("text");
162 let value = with(ns_string, crate::objc::sel(c"stringWithUTF8String:"), c.as_ptr());
163 let kind = with(
164 ns_string,
165 crate::objc::sel(c"stringWithUTF8String:"),
166 c"public.plain-text".as_ptr(),
167 );
168 type Init = unsafe extern "C" fn(
169 *mut c_void, *const c_void, *mut c_void, *mut c_void,
170 ) -> *mut c_void;
171 let init: Init = core::mem::transmute(crate::objc::msg_send_addr());
172 let p = init(obj, crate::objc::sel(c"initWithItem:typeIdentifier:"), value, kind);
173 assert!(!p.is_null(), "NSItemProvider for {text:?}");
174 Self(p)
175 }
176 }
177}
178
179pub(crate) unsafe fn read_provider_array(array: u64) -> Vec<ItemProvider> {
181 unsafe {
182 if array == 0 {
183 return Vec::new();
184 }
185 let stride = abi::type_bytes(crate::type_by_name!(swift_type!(objc NSItemProvider).as_bytes()));
186 let (base, count) = abi::array_parts(array);
187 (0..count)
188 .map(|i| ItemProvider(*(base.add(i * stride) as *const *mut c_void)))
189 .collect()
190 }
191}
192
193pub fn on_copy_command(view: &ViewHandle, items: &[&str]) -> ViewHandle {
195 unsafe {
196 let c = word_closure(emit_providers, provider_payload(provider_array(items), None, None));
197 opaque_modifier(
198 resolve::on_copy_command_fn(),
199 resolve::on_copy_command_oret(),
200 view,
201 &c,
202 [0.0; 4],
203 )
204 }
205}
206
207pub fn on_cut_command(view: &ViewHandle, items: &[&str]) -> ViewHandle {
209 unsafe {
210 let c = word_closure(emit_providers, provider_payload(provider_array(items), None, None));
211 opaque_modifier(
212 resolve::on_cut_command_fn(),
213 resolve::on_cut_command_oret(),
214 view,
215 &c,
216 [0.0; 4],
217 )
218 }
219}
220
221pub fn item_provider(view: &ViewHandle, item: Option<&str>) -> ViewHandle {
223 unsafe {
224 let array = provider_array(&item.map_or(Vec::new(), |t| vec![t]));
225 let c = word_closure(emit_first_provider, provider_payload(array, None, None));
226 opaque_modifier(
227 resolve::item_provider_fn(),
228 resolve::item_provider_oret(),
229 view,
230 &c,
231 [0.0; 4],
232 )
233 }
234}
235
236pub fn imports_item_providers(
238 view: &ViewHandle,
239 kinds: &[ContentType],
240 accept: impl Fn(&[ItemProvider]) -> bool + 'static,
241) -> ViewHandle {
242 unsafe {
243 let types = content_types(kinds);
244 let c = word_closure(decide_on_providers, provider_payload(0, Some(Box::new(accept)), None));
245 opaque_modifier(
246 resolve::imports_item_providers_fn(),
247 resolve::imports_item_providers_oret(),
248 view,
249 &[types, c[0], c[1]],
250 [0.0; 4],
251 )
252 }
253}
254
255pub fn on_paste_command(
257 view: &ViewHandle,
258 kinds: &[ContentType],
259 paste: impl Fn(&[ItemProvider]) + 'static,
260) -> ViewHandle {
261 unsafe {
262 let types = content_types(kinds);
263 let c = wide_words(observe_providers, provider_payload(0, None, Some(Box::new(paste))));
264 opaque_modifier(
265 resolve::on_paste_command_fn(),
266 resolve::on_paste_command_oret(),
267 view,
268 &[types, c[0], c[1]],
269 [0.0; 4],
270 )
271 }
272}
273
274pub fn exports_item_providers(
279 view: &ViewHandle,
280 kinds: &[ContentType],
281 items: &[&str],
282 edit: impl Fn(&[ItemProvider]) -> bool + 'static,
283) -> ViewHandle {
284 unsafe {
285 let types = content_types(kinds);
286 let export =
287 word_closure(emit_providers, provider_payload(provider_array(items), None, None));
288 let on_edit = word_closure(decide_on_providers, provider_payload(0, Some(Box::new(edit)), None));
293 opaque_modifier(
294 resolve::exports_item_providers_fn(),
295 resolve::exports_item_providers_oret(),
296 view,
297 &[types, export[0], export[1], on_edit[0], on_edit[1]],
298 [0.0; 4],
299 )
300 }
301}
302
303pub fn on_drop(
307 view: &ViewHandle,
308 kinds: &[ContentType],
309 accept: impl Fn(&[ItemProvider]) -> bool + 'static,
310) -> ViewHandle {
311 unsafe {
312 let types = content_types(kinds);
313 let payload = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL]).as_bytes());
316 let opt = abi::resolve_type_by_mangled_name(mangle::optional(&mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL])).as_bytes());
317 let mut nil = vec![0u8; abi::type_bytes(opt).max(24)];
318 abi::store_optional_tag(&mut nil, payload, false);
319 let b = binding_words(&mut nil);
320 let perform = word_closure(decide_on_providers, provider_payload(0, Some(Box::new(accept)), None));
321 opaque_modifier(
322 resolve::on_drop_fn(),
323 resolve::on_drop_oret(),
324 view,
325 &[types, b[0], b[1], b[2], perform[0], perform[1]],
326 [0.0; 4],
327 )
328 }
329}
330
331#[derive(Clone, Copy, Debug, PartialEq, Eq)]
333pub enum DropOperation {
334 Cancel = 0,
335 Forbidden = 1,
336 Copy = 2,
337 Move = 3,
338}
339
340pub(crate) unsafe extern "C" fn emit_drop_configuration(out: *mut u8, payload: *mut c_void) {
346 unsafe {
347 let f = boxed_ref::<dyn Fn() -> DropOperation>(payload);
348 let op = abi::enum_argument(abi::metadata0(resolve::drop_operation_ma()), f() as u32);
349 let meta = abi::metadata0(resolve::drop_configuration_ma());
350 core::arch::asm!("blr {f}", f = in(reg) resolve::drop_configuration_init(),
351 in("x0") op, in("x8") out, in("x20") meta,
352 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
353 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
354 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
355 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
356 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
357 );
358 }
359}
360
361pub fn drop_configuration(view: &ViewHandle, decide: impl Fn() -> DropOperation + 'static) -> ViewHandle {
363 unsafe {
364 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
365 emit_drop_configuration,
366 boxed_ctx(Box::new(decide) as Box<dyn Fn() -> DropOperation>),
367 ))) as *mut abi::ClosureCtx;
368 opaque_modifier(
369 resolve::drop_configuration_fn(),
370 resolve::drop_configuration_oret(),
371 view,
372 &[abi::swift_closure_thunk as *const () as u64, ctx as u64],
373 [0.0; 4],
374 )
375 }
376}
377
378#[derive(Clone, Copy, Debug, PartialEq, Eq)]
380pub struct DragOperations {
381 pub copy: bool,
382 pub move_: bool,
383 pub delete: bool,
384}
385
386unsafe fn drag_operations(ma: *const c_void, init: *const c_void, ops: DragOperations) -> Vec<u8> {
391 unsafe {
392 let meta = abi::metadata0(ma);
393 let mut out = vec![0u8; abi::type_bytes(meta)];
394 abi::call_out(
395 init,
396 abi::ints(&[u64::from(ops.copy), u64::from(ops.move_), u64::from(ops.delete)]),
397 [0.0; 4],
398 meta as u64,
399 out.as_mut_ptr(),
400 );
401 out
402 }
403}
404
405pub fn drag_configuration(
411 view: &ViewHandle,
412 within_app: DragOperations,
413 outside_app: DragOperations,
414) -> ViewHandle {
415 unsafe {
416 let within = drag_operations(
417 resolve::drag_ops_within_ma(), resolve::drag_ops_within_init(), within_app);
418 let outside = drag_operations(
419 resolve::drag_ops_outside_ma(), resolve::drag_ops_outside_init(), outside_app);
420 let meta = abi::metadata0(resolve::drag_configuration_ma());
421 let mut config = vec![0u8; abi::type_bytes(meta)];
422 core::arch::asm!("blr {f}", f = in(reg) resolve::drag_configuration_init(),
423 in("x0") within.as_ptr(), in("x1") outside.as_ptr(),
424 in("x8") config.as_mut_ptr(), in("x20") meta,
425 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
426 lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
427 lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
428 lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
429 lateout("x21") _, clobber_abi("C"),
430 );
431 opaque_modifier(
432 resolve::drag_configuration_fn(),
433 resolve::drag_configuration_oret(),
434 view,
435 &[config.as_ptr() as u64],
436 [0.0; 4],
437 )
438 }
439}
440
441pub(crate) unsafe extern "C" fn emit_provider(payload: *mut c_void, _arg: u64) -> u64 {
443 unsafe {
444 let text = &*(payload as *const String);
445 ItemProvider::plain_text(text).0 as u64
446 }
447}
448
449pub fn on_drag(view: &ViewHandle, item: &str, preview: &ViewHandle) -> ViewHandle {
459 unsafe {
460 let text = Box::leak(Box::new(item.to_string())) as *mut String;
461 let data = word_closure(emit_provider, text as *mut c_void);
462 let none_any = [0u64; 4];
468 let mut ints = vec![1u64, none_any.as_ptr() as u64];
469 ints.extend_from_slice(&data);
470 ints.extend_from_slice(&[0, 0, 0, 0]);
473 ints.extend_from_slice(&view_words(preview));
474 generic1_modifier_ints(
475 resolve::on_drag_fn(),
476 resolve::on_drag_oret(),
477 view,
478 preview.meta,
479 preview.wt,
480 &ints,
481 [0.0; 4],
482 )
483 }
484}
485
486
487unsafe extern "C" fn loaded_item(block: *mut c_void, item: *mut c_void, error: *mut c_void) {
495 unsafe {
496 let capture = (*(block as *const crate::objc::CapturingBlock)).capture;
497 if capture.is_null() {
498 return;
499 }
500 let then = Box::from_raw(capture as *mut Box<dyn FnOnce(Option<String>)>);
501 let text = (error.is_null() && !item.is_null())
502 .then(|| item_text(item))
503 .flatten();
504 then(text);
505 }
506}
507
508unsafe fn item_text(item: *mut c_void) -> Option<String> {
513 unsafe {
514 let _pool = crate::objc::AutoreleasePool::new();
515 if crate::objc::responds(item, crate::objc::sel(c"UTF8String")) {
518 let p = crate::objc::send(item, crate::objc::sel(c"UTF8String")) as *const i8;
519 if p.is_null() {
520 return None;
521 }
522 return Some(core::ffi::CStr::from_ptr(p).to_string_lossy().into_owned());
523 }
524 if crate::objc::responds(item, crate::objc::sel(c"bytes")) {
525 let bytes = crate::objc::send(item, crate::objc::sel(c"bytes")) as *const u8;
526 let len = crate::objc::send(item, crate::objc::sel(c"length")) as usize;
527 if bytes.is_null() || len == 0 {
528 return None;
529 }
530 return Some(String::from_utf8_lossy(core::slice::from_raw_parts(bytes, len)).into_owned());
531 }
532 None
533 }
534}