use super::*;
use super::closures::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::{self, swift_type};
pub fn on_drag_session_updated(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_drag_session_updated_fn(),
resolve::on_drag_session_updated_oret(),
view,
&ignoring_value_words(action),
[0.0; 4],
)
}
}
pub fn on_drop_session_updated(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_drop_session_updated_fn(),
resolve::on_drop_session_updated_oret(),
view,
&ignoring_value_words(action),
[0.0; 4],
)
}
}
pub(crate) unsafe fn content_types(kinds: &[ContentType]) -> u64 {
unsafe {
let meta = crate::type_by_name!(swift_type!(UniformTypeIdentifiers.UTType).as_bytes());
let stride = abi::type_bytes(meta);
let mut bytes = vec![0u8; stride * kinds.len()];
for (i, k) in kinds.iter().enumerate() {
let v = resilient_static(meta, k.getter());
bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
}
abi::swift_array(meta, kinds.len(), &bytes)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ItemProvider(pub(crate) *mut c_void);
impl ItemProvider {
pub fn as_ptr(&self) -> *mut c_void {
self.0
}
pub fn type_identifiers(&self) -> Vec<String> {
unsafe {
type Send0 = unsafe extern "C" fn(*mut c_void, *const c_void) -> *mut c_void;
let send: Send0 = core::mem::transmute(crate::objc::msg_send_addr());
let array = send(self.0, crate::objc::sel(c"registeredTypeIdentifiers"));
if array.is_null() {
return Vec::new();
}
type Count = unsafe extern "C" fn(*mut c_void, *const c_void) -> usize;
let count: Count = core::mem::transmute(crate::objc::msg_send_addr());
let n = count(array, crate::objc::sel(c"count"));
type At = unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void;
let at: At = core::mem::transmute(crate::objc::msg_send_addr());
type Utf8 = unsafe extern "C" fn(*mut c_void, *const c_void) -> *const i8;
let utf8: Utf8 = core::mem::transmute(crate::objc::msg_send_addr());
(0..n)
.map(|i| {
let s = at(array, crate::objc::sel(c"objectAtIndex:"), i);
let p = utf8(s, crate::objc::sel(c"UTF8String"));
if p.is_null() {
String::new()
} else {
core::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
}
})
.collect()
}
}
pub fn load_plain_text(&self, then: impl FnOnce(Option<String>) + 'static) {
unsafe {
let boxed: Box<dyn FnOnce(Option<String>)> = Box::new(then);
let capture = Box::into_raw(Box::new(boxed)) as *mut c_void;
let block = Box::leak(Box::new(crate::objc::CapturingBlock::new(
loaded_item as *const c_void,
capture,
))) as *const crate::objc::CapturingBlock;
let kind = crate::objc::nsstring("public.plain-text");
type Load = unsafe extern "C" fn(
*mut c_void, *const c_void, *mut c_void, *mut c_void,
*const crate::objc::CapturingBlock,
) -> *mut c_void;
let load: Load = core::mem::transmute(crate::objc::msg_send_addr());
load(
self.0,
crate::objc::sel(c"loadItemForTypeIdentifier:options:completionHandler:"),
kind,
core::ptr::null_mut(),
block,
);
}
}
pub fn plain_text(text: &str) -> Self {
unsafe {
let cls = crate::objc::class_named(c"NSItemProvider");
type Alloc = unsafe extern "C" fn(*mut c_void, *const c_void) -> *mut c_void;
let alloc: Alloc = core::mem::transmute(crate::objc::msg_send_addr());
let obj = alloc(cls, crate::objc::sel(c"alloc"));
let ns_string = crate::objc::class_named(c"NSString");
type With = unsafe extern "C" fn(*mut c_void, *const c_void, *const i8) -> *mut c_void;
let with: With = core::mem::transmute(crate::objc::msg_send_addr());
let c = std::ffi::CString::new(text).expect("text");
let value = with(ns_string, crate::objc::sel(c"stringWithUTF8String:"), c.as_ptr());
let kind = with(
ns_string,
crate::objc::sel(c"stringWithUTF8String:"),
c"public.plain-text".as_ptr(),
);
type Init = unsafe extern "C" fn(
*mut c_void, *const c_void, *mut c_void, *mut c_void,
) -> *mut c_void;
let init: Init = core::mem::transmute(crate::objc::msg_send_addr());
let p = init(obj, crate::objc::sel(c"initWithItem:typeIdentifier:"), value, kind);
assert!(!p.is_null(), "NSItemProvider for {text:?}");
Self(p)
}
}
}
pub(crate) unsafe fn read_provider_array(array: u64) -> Vec<ItemProvider> {
unsafe {
if array == 0 {
return Vec::new();
}
let stride = abi::type_bytes(crate::type_by_name!(swift_type!(objc NSItemProvider).as_bytes()));
let (base, count) = abi::array_parts(array);
(0..count)
.map(|i| ItemProvider(*(base.add(i * stride) as *const *mut c_void)))
.collect()
}
}
pub fn on_copy_command(view: &ViewHandle, items: &[&str]) -> ViewHandle {
unsafe {
let c = word_closure(emit_providers, provider_payload(provider_array(items), None, None));
opaque_modifier(
resolve::on_copy_command_fn(),
resolve::on_copy_command_oret(),
view,
&c,
[0.0; 4],
)
}
}
pub fn on_cut_command(view: &ViewHandle, items: &[&str]) -> ViewHandle {
unsafe {
let c = word_closure(emit_providers, provider_payload(provider_array(items), None, None));
opaque_modifier(
resolve::on_cut_command_fn(),
resolve::on_cut_command_oret(),
view,
&c,
[0.0; 4],
)
}
}
pub fn item_provider(view: &ViewHandle, item: Option<&str>) -> ViewHandle {
unsafe {
let array = provider_array(&item.map_or(Vec::new(), |t| vec![t]));
let c = word_closure(emit_first_provider, provider_payload(array, None, None));
opaque_modifier(
resolve::item_provider_fn(),
resolve::item_provider_oret(),
view,
&c,
[0.0; 4],
)
}
}
pub fn imports_item_providers(
view: &ViewHandle,
kinds: &[ContentType],
accept: impl Fn(&[ItemProvider]) -> bool + 'static,
) -> ViewHandle {
unsafe {
let types = content_types(kinds);
let c = word_closure(decide_on_providers, provider_payload(0, Some(Box::new(accept)), None));
opaque_modifier(
resolve::imports_item_providers_fn(),
resolve::imports_item_providers_oret(),
view,
&[types, c[0], c[1]],
[0.0; 4],
)
}
}
pub fn on_paste_command(
view: &ViewHandle,
kinds: &[ContentType],
paste: impl Fn(&[ItemProvider]) + 'static,
) -> ViewHandle {
unsafe {
let types = content_types(kinds);
let c = wide_words(observe_providers, provider_payload(0, None, Some(Box::new(paste))));
opaque_modifier(
resolve::on_paste_command_fn(),
resolve::on_paste_command_oret(),
view,
&[types, c[0], c[1]],
[0.0; 4],
)
}
}
pub fn exports_item_providers(
view: &ViewHandle,
kinds: &[ContentType],
items: &[&str],
edit: impl Fn(&[ItemProvider]) -> bool + 'static,
) -> ViewHandle {
unsafe {
let types = content_types(kinds);
let export =
word_closure(emit_providers, provider_payload(provider_array(items), None, None));
let on_edit = word_closure(decide_on_providers, provider_payload(0, Some(Box::new(edit)), None));
opaque_modifier(
resolve::exports_item_providers_fn(),
resolve::exports_item_providers_oret(),
view,
&[types, export[0], export[1], on_edit[0], on_edit[1]],
[0.0; 4],
)
}
}
pub fn on_drop(
view: &ViewHandle,
kinds: &[ContentType],
accept: impl Fn(&[ItemProvider]) -> bool + 'static,
) -> ViewHandle {
unsafe {
let types = content_types(kinds);
let payload = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL]).as_bytes());
let opt = abi::resolve_type_by_mangled_name(mangle::optional(&mangle::bound("7SwiftUI7BindingV", &[mangle::BOOL])).as_bytes());
let mut nil = vec![0u8; abi::type_bytes(opt).max(24)];
abi::store_optional_tag(&mut nil, payload, false);
let b = binding_words(&mut nil);
let perform = word_closure(decide_on_providers, provider_payload(0, Some(Box::new(accept)), None));
opaque_modifier(
resolve::on_drop_fn(),
resolve::on_drop_oret(),
view,
&[types, b[0], b[1], b[2], perform[0], perform[1]],
[0.0; 4],
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DropOperation {
Cancel = 0,
Forbidden = 1,
Copy = 2,
Move = 3,
}
pub(crate) unsafe extern "C" fn emit_drop_configuration(out: *mut u8, payload: *mut c_void) {
unsafe {
let f = boxed_ref::<dyn Fn() -> DropOperation>(payload);
let op = abi::enum_argument(abi::metadata0(resolve::drop_operation_ma()), f() as u32);
let meta = abi::metadata0(resolve::drop_configuration_ma());
core::arch::asm!("blr {f}", f = in(reg) resolve::drop_configuration_init(),
in("x0") op, in("x8") out, in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
}
pub fn drop_configuration(view: &ViewHandle, decide: impl Fn() -> DropOperation + 'static) -> ViewHandle {
unsafe {
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
emit_drop_configuration,
boxed_ctx(Box::new(decide) as Box<dyn Fn() -> DropOperation>),
))) as *mut abi::ClosureCtx;
opaque_modifier(
resolve::drop_configuration_fn(),
resolve::drop_configuration_oret(),
view,
&[abi::swift_closure_thunk as *const () as u64, ctx as u64],
[0.0; 4],
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DragOperations {
pub copy: bool,
pub move_: bool,
pub delete: bool,
}
unsafe fn drag_operations(ma: *const c_void, init: *const c_void, ops: DragOperations) -> Vec<u8> {
unsafe {
let meta = abi::metadata0(ma);
let mut out = vec![0u8; abi::type_bytes(meta)];
abi::call_out(
init,
abi::ints(&[u64::from(ops.copy), u64::from(ops.move_), u64::from(ops.delete)]),
[0.0; 4],
meta as u64,
out.as_mut_ptr(),
);
out
}
}
pub fn drag_configuration(
view: &ViewHandle,
within_app: DragOperations,
outside_app: DragOperations,
) -> ViewHandle {
unsafe {
let within = drag_operations(
resolve::drag_ops_within_ma(), resolve::drag_ops_within_init(), within_app);
let outside = drag_operations(
resolve::drag_ops_outside_ma(), resolve::drag_ops_outside_init(), outside_app);
let meta = abi::metadata0(resolve::drag_configuration_ma());
let mut config = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::drag_configuration_init(),
in("x0") within.as_ptr(), in("x1") outside.as_ptr(),
in("x8") config.as_mut_ptr(), in("x20") meta,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
opaque_modifier(
resolve::drag_configuration_fn(),
resolve::drag_configuration_oret(),
view,
&[config.as_ptr() as u64],
[0.0; 4],
)
}
}
pub(crate) unsafe extern "C" fn emit_provider(payload: *mut c_void, _arg: u64) -> u64 {
unsafe {
let text = &*(payload as *const String);
ItemProvider::plain_text(text).0 as u64
}
}
pub fn on_drag(view: &ViewHandle, item: &str, preview: &ViewHandle) -> ViewHandle {
unsafe {
let text = Box::leak(Box::new(item.to_string())) as *mut String;
let data = word_closure(emit_provider, text as *mut c_void);
let none_any = [0u64; 4];
let mut ints = vec![1u64, none_any.as_ptr() as u64];
ints.extend_from_slice(&data);
ints.extend_from_slice(&[0, 0, 0, 0]);
ints.extend_from_slice(&view_words(preview));
generic1_modifier_ints(
resolve::on_drag_fn(),
resolve::on_drag_oret(),
view,
preview.meta,
preview.wt,
&ints,
[0.0; 4],
)
}
}
unsafe extern "C" fn loaded_item(block: *mut c_void, item: *mut c_void, error: *mut c_void) {
unsafe {
let capture = (*(block as *const crate::objc::CapturingBlock)).capture;
if capture.is_null() {
return;
}
let then = Box::from_raw(capture as *mut Box<dyn FnOnce(Option<String>)>);
let text = (error.is_null() && !item.is_null())
.then(|| item_text(item))
.flatten();
then(text);
}
}
unsafe fn item_text(item: *mut c_void) -> Option<String> {
unsafe {
let _pool = crate::objc::AutoreleasePool::new();
if crate::objc::responds(item, crate::objc::sel(c"UTF8String")) {
let p = crate::objc::send(item, crate::objc::sel(c"UTF8String")) as *const i8;
if p.is_null() {
return None;
}
return Some(core::ffi::CStr::from_ptr(p).to_string_lossy().into_owned());
}
if crate::objc::responds(item, crate::objc::sel(c"bytes")) {
let bytes = crate::objc::send(item, crate::objc::sel(c"bytes")) as *const u8;
let len = crate::objc::send(item, crate::objc::sel(c"length")) as usize;
if bytes.is_null() || len == 0 {
return None;
}
return Some(String::from_utf8_lossy(core::slice::from_raw_parts(bytes, len)).into_owned());
}
None
}
}