use super::*;
use crate::abi;
use crate::mangle::{self, swift_type};
use crate::resolve;
use core::ffi::c_void;
#[cfg(target_os = "macos")]
unsafe fn container_meta(
ma: *const c_void,
content: &ViewHandle,
) -> (*const c_void, *const c_void) {
unsafe {
let meta = abi::generic_metadata2(ma, content.meta, content.wt);
assert!(!meta.is_null(), "container metadata instantiation failed");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
(meta, wt)
}
}
#[cfg(target_os = "macos")]
unsafe fn one_child_container(
init: *const c_void,
ma: *const c_void,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let (meta, wt) = container_meta(ma, content);
let child = content.clone();
let kid = Child::new(&child, Content::Concrete);
let handles = kid.element();
let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
let mut ctx =
abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x0") abi::swift_closure_thunk as *const c_void,
in("x1") &mut ctx as *mut abi::ClosureCtx,
in("x2") content.meta,
in("x3") content.wt,
in("x8") result.as_mut_ptr(),
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("x20") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
#[cfg(target_os = "macos")]
pub fn hsplit_view(content: &ViewHandle) -> ViewHandle {
unsafe {
one_child_container(resolve::hsplit_view_init(), resolve::hsplit_view_ma(), content)
}
}
#[cfg(target_os = "macos")]
pub fn vsplit_view(content: &ViewHandle) -> ViewHandle {
unsafe {
one_child_container(resolve::vsplit_view_init(), resolve::vsplit_view_ma(), content)
}
}
unsafe fn no_argument_view(init: *const c_void, named: &str) -> ViewHandle {
unsafe {
let meta = abi::resolve_type_by_mangled_name(named.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(named));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x8") result.as_mut_ptr(),
lateout("x0") _, 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("x20") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub fn rename_button() -> ViewHandle {
unsafe {
let label = mangle::bound(
&swift_type!(SwiftUI.Label),
&[&swift_type!(SwiftUI.Text), &swift_type!(SwiftUI.Image)],
);
let named = mangle::bound(&swift_type!(SwiftUI.RenameButton), &[&label]);
no_argument_view(resolve::rename_button_init(), &named)
}
}
#[cfg(target_os = "macos")]
pub fn settings_link() -> ViewHandle {
unsafe {
let named = mangle::bound(
&swift_type!(SwiftUI.SettingsLink),
&[&swift_type!(SwiftUI.DefaultSettingsLinkLabel)],
);
no_argument_view(resolve::settings_link_init(), &named)
}
}
pub fn content_unavailable(title: &str, system_image: &str, description: Option<&str>) -> ViewHandle {
unsafe {
let label = mangle::bound(
&swift_type!(SwiftUI.Label),
&[&swift_type!(SwiftUI.Text), &swift_type!(SwiftUI.Image)],
);
let named = mangle::bound(
&swift_type!(SwiftUI.ContentUnavailableView),
&[
&label,
&mangle::optional(&swift_type!(SwiftUI.Text)),
&swift_type!(SwiftUI.EmptyView),
],
);
let meta = abi::resolve_type_by_mangled_name(named.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(&named));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let (k0, k1, k2, k3) = lsk_words(title);
let img = abi::swift_string(system_image);
let i0 = u64::from_le_bytes(img[..8].try_into().expect("image lo"));
let i1 = u64::from_le_bytes(img[8..].try_into().expect("image hi"));
let text = match description {
Some(s) => text_value(s),
None => [0u64; 4],
};
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!(
"sub sp, sp, #16",
"stp {t2}, {t3}, [sp]",
"blr {f}",
"add sp, sp, #16",
f = in(reg) resolve::content_unavailable_system_init(),
t2 = in(reg) text[2],
t3 = in(reg) text[3],
in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
in("x4") i0, in("x5") i1,
in("x6") text[0], in("x7") text[1],
in("x8") result.as_mut_ptr(),
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub struct ScrollProxy {
bytes: Vec<u8>,
meta: *const c_void,
}
impl Clone for ScrollProxy {
fn clone(&self) -> Self {
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
let mut bytes = vec![0u8; self.bytes.len()];
(vwt.initialize_with_copy)(
bytes.as_mut_ptr() as *mut c_void,
self.bytes.as_ptr() as *mut c_void,
self.meta,
);
Self { bytes, meta: self.meta }
}
}
}
impl Drop for ScrollProxy {
fn drop(&mut self) {
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
if vwt.size > 0 {
(vwt.destroy)(self.bytes.as_mut_ptr() as *mut c_void, self.meta);
}
}
}
}
impl ScrollProxy {
unsafe fn copy_from(raw: *mut c_void) -> Self {
unsafe {
let meta = abi::metadata0(resolve::scroll_view_proxy_ma());
assert!(!meta.is_null(), "ScrollViewProxy metadata");
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(meta);
let mut bytes = vec![0u8; abi::type_bytes(meta)];
(vwt.initialize_with_copy)(bytes.as_mut_ptr() as *mut c_void, raw, meta);
Self { bytes, meta }
}
}
pub fn scroll_to(&self, id: i64) {
unsafe { self.scroll(id, None) }
}
pub fn scroll_to_anchor(&self, id: i64, anchor: (f64, f64)) {
unsafe { self.scroll(id, Some(anchor)) }
}
unsafe fn scroll(&self, id: i64, anchor: Option<(f64, f64)>) {
unsafe {
let mut value = id;
let (x, y, tag) = match anchor {
Some((x, y)) => (x.to_bits(), y.to_bits(), 0u64),
None => (0, 0, 1),
};
core::arch::asm!("blr {f}", f = in(reg) resolve::scroll_to_fn(),
in("x0") &mut value as *mut i64,
in("x1") x, in("x2") y, in("x3") tag,
in("x4") resolve::int_meta(),
in("x5") resolve::int_hashable_wt(),
in("x20") self.bytes.as_ptr(),
lateout("x6") _, lateout("x7") _, lateout("x8") _,
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"),
);
}
}
}
struct ProxyPayload {
builder: Box<dyn Fn(ScrollProxy) -> ViewHandle>,
}
unsafe extern "C" fn emit_with_proxy(out: *mut u8, proxy: i64, payload: *mut c_void) {
let p = unsafe { &*(payload as *const ProxyPayload) };
let view = (p.builder)(unsafe { ScrollProxy::copy_from(proxy as *mut c_void) });
unsafe { *(out as *mut u64) = view.into_anyview_word() };
}
pub fn scroll_view_reader(
content: impl Fn(ScrollProxy) -> ViewHandle + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::generic_metadata2(
resolve::scroll_view_reader_ma(),
resolve::anyview_meta(),
resolve::anyview_wt(),
);
assert!(!meta.is_null(), "ScrollViewReader metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let payload = Box::leak(Box::new(ProxyPayload { builder: Box::new(content) }))
as *mut ProxyPayload as *mut c_void;
let ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(emit_with_proxy, payload)))
as *mut abi::IndexClosureCtx;
let stride = abi::type_bytes(meta);
assert_eq!(stride, 16, "ScrollViewReader is no longer two words");
let (w0, w1): (u64, u64);
core::arch::asm!("blr {f}", f = in(reg) resolve::scroll_view_reader_init(),
in("x0") abi::index_closure_thunk as *const c_void,
in("x1") ctx,
in("x2") resolve::anyview_meta(),
in("x3") resolve::anyview_wt(),
lateout("x0") w0, lateout("x1") w1,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
let mut result = vec![0u8; stride];
result[..8].copy_from_slice(&w0.to_le_bytes());
result[8..16].copy_from_slice(&w1.to_le_bytes());
wrap_owned(result, meta, wt)
}
}
pub fn paste_button(
kinds: &[ContentType],
paste: impl Fn(&[ItemProvider]) + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::paste_button_ma());
assert!(!meta.is_null(), "PasteButton metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let types = content_types(kinds);
let c = wide_words(observe_providers, provider_payload(0, None, Some(Box::new(paste))));
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::paste_button_init(),
in("x0") types,
in("x1") c[0],
in("x2") c[1],
in("x8") result.as_mut_ptr(),
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("x20") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub fn on_insert(
content: &ViewHandle,
kinds: &[ContentType],
action: impl Fn(i64, &[ItemProvider]) + 'static,
) -> ViewHandle {
unsafe {
let dvc = abi::get_view_wt_for_proto(content.meta, resolve::dynamic_view_content_proto());
assert!(
!dvc.is_null(),
"{} !: DynamicViewContent — `on_insert` needs a ForEach, not any view",
crate::diag::type_name(content.meta),
);
let types = content_types(kinds);
let c = pair_words(
insert_at_index,
Box::leak(Box::new(InsertPayload { action: Box::new(action) })) as *mut InsertPayload
as *mut c_void,
);
let (meta, _dvc_out, size) =
super::opaque_result_type(resolve::on_insert_oret(), &[content.meta, dvc]);
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let mut result = vec![0u8; size];
let this = content.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::on_insert_fn(),
in("x0") types,
in("x1") c[0],
in("x2") c[1],
in("x3") content.meta,
in("x4") dvc,
in("x8") result.as_mut_ptr(),
in("x20") this,
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"),
);
wrap_owned(result, meta, wt)
}
}
type InsertAction = Box<dyn Fn(i64, &[ItemProvider])>;
struct InsertPayload {
action: InsertAction,
}
unsafe extern "C" fn insert_at_index(index: *const u8, providers: *const u8, payload: *mut c_void) {
let p = unsafe { &*(payload as *const InsertPayload) };
let items = unsafe { read_provider_array(providers as u64) };
(p.action)(index as i64, &items);
}
pub fn subscription_view(
content: &ViewHandle,
complete_immediately: bool,
action: impl Fn(i64) + 'static,
) -> ViewHandle {
unsafe {
let empty = mangle::bound("7Combine5EmptyV", &[mangle::INT, mangle::NEVER]);
let publisher = abi::resolve_type_by_mangled_name(empty.as_bytes());
assert!(!publisher.is_null(), "{}", mangle::demangle(&empty));
let pub_wt = abi::get_view_wt_for_proto(publisher, resolve::publisher_proto());
assert!(!pub_wt.is_null(), "Empty<Int, Never> !: Combine.Publisher");
let named = mangle::bound(
&swift_type!(SwiftUI.SubscriptionView),
&[&empty, &swift_type!(SwiftUI.AnyView)],
);
let meta = abi::resolve_type_by_mangled_name(named.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(&named));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let mut value = vec![u8::from(complete_immediately); abi::type_bytes(publisher).max(1)];
let mut erased_word = content.to_anyview_word().to_le_bytes();
let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
super::saw_published_int,
super::boxed_ctx(Box::new(action) as Box<dyn Fn(i64)>),
))) as *mut abi::ValueClosureCtx;
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::subscription_view_init(),
in("x0") erased_word.as_mut_ptr(),
in("x1") value.as_mut_ptr(),
in("x2") abi::value_closure_thunk as *const c_void,
in("x3") ctx,
in("x4") publisher,
in("x5") resolve::anyview_meta(),
in("x6") pub_wt,
in("x7") resolve::anyview_wt(),
in("x8") result.as_mut_ptr(),
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AppleButtonLabel {
SignIn,
SignUp,
Continue,
}
impl AppleButtonLabel {
fn getter(self) -> &'static core::ffi::CStr {
match self {
Self::SignIn => c"$s31_AuthenticationServices_SwiftUI21SignInWithAppleButtonV5LabelV04signF0AEvgZ",
Self::SignUp => c"$s31_AuthenticationServices_SwiftUI21SignInWithAppleButtonV5LabelV6signUpAEvgZ",
Self::Continue => c"$s31_AuthenticationServices_SwiftUI21SignInWithAppleButtonV5LabelV8continueAEvgZ",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AppleButtonStyle {
Black,
White,
WhiteOutline,
}
impl AppleButtonStyle {
fn getter(self) -> &'static core::ffi::CStr {
match self {
Self::Black => c"$s31_AuthenticationServices_SwiftUI21SignInWithAppleButtonV5StyleV5blackAEvgZ",
Self::White => c"$s31_AuthenticationServices_SwiftUI21SignInWithAppleButtonV5StyleV5whiteAEvgZ",
Self::WhiteOutline => c"$s31_AuthenticationServices_SwiftUI21SignInWithAppleButtonV5StyleV12whiteOutlineAEvgZ",
}
}
}
pub struct AppleIDRequest<'a> {
raw: *mut c_void,
marker: core::marker::PhantomData<&'a ()>,
}
impl AppleIDRequest<'_> {
pub fn request_full_name_and_email(&self) {
unsafe {
let full_name = crate::objc::nsstring("fullName");
let email = crate::objc::nsstring("email");
let items = [full_name, email];
let array_cls = crate::objc::class(c"OBJC_CLASS_$_NSArray");
assert!(!array_cls.is_null(), "NSArray class");
type WithObjects =
unsafe extern "C" fn(*mut c_void, *const c_void, *const *mut c_void, u64) -> *mut c_void;
let f: WithObjects =
core::mem::transmute(crate::objc::msg_send_addr());
let array = f(
array_cls,
crate::objc::sel(c"arrayWithObjects:count:"),
items.as_ptr(),
2,
);
crate::objc::send_id(self.raw, crate::objc::sel(c"setRequestedScopes:"), array);
}
}
pub fn raw(&self) -> *mut c_void {
self.raw
}
}
struct ApplePayload {
on_request: Box<dyn Fn(AppleIDRequest<'_>)>,
on_completion: Box<dyn Fn(bool)>,
result_meta: *const c_void,
}
unsafe extern "C" fn apple_on_request(request: *const u8, payload: *mut c_void) {
let p = unsafe { &*(payload as *const ApplePayload) };
(p.on_request)(AppleIDRequest {
raw: request as *mut c_void,
marker: core::marker::PhantomData,
});
}
unsafe extern "C" fn apple_on_completion(result: *const u8, payload: *mut c_void) {
let p = unsafe { &*(payload as *const ApplePayload) };
let tag = unsafe { abi::enum_tag(result as *const c_void, p.result_meta) };
(p.on_completion)(tag == 0);
}
pub fn sign_in_with_apple_button(
label: AppleButtonLabel,
on_request: impl Fn(AppleIDRequest<'_>) + 'static,
on_completion: impl Fn(bool) + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::siwa_button_ma());
assert!(!meta.is_null(), "SignInWithAppleButton metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let label_meta = abi::metadata0(resolve::siwa_label_ma());
assert!(!label_meta.is_null(), "SignInWithAppleButton.Label metadata");
let getter = resolve::sym_in(resolve::AUTH_SWIFTUI, label.getter());
assert!(!getter.is_null(), "{:?}", label.getter());
let mut label_value = static_getter_indirect(getter, label_meta, 0);
let result_named = mangle::bound(
"s6ResultO",
&["So15ASAuthorizationC", "s5Error_p"],
);
let result_meta = abi::resolve_type_by_mangled_name(result_named.as_bytes());
assert!(!result_meta.is_null(), "{}", mangle::demangle(&result_named));
let payload = Box::leak(Box::new(ApplePayload {
on_request: Box::new(on_request),
on_completion: Box::new(on_completion),
result_meta,
})) as *mut ApplePayload as *mut c_void;
let req_ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(apple_on_request, payload)))
as *mut abi::ValueClosureCtx;
let done_ctx =
Box::leak(Box::new(abi::ValueClosureCtx::new(apple_on_completion, payload)))
as *mut abi::ValueClosureCtx;
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::siwa_button_init(),
in("x0") label_value.as_mut_ptr(),
in("x1") abi::value_closure_thunk as *const c_void,
in("x2") req_ctx,
in("x3") abi::value_closure_thunk as *const c_void,
in("x4") done_ctx,
in("x8") result.as_mut_ptr(),
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("x20") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub fn sign_in_with_apple_button_style(
view: &ViewHandle,
style: AppleButtonStyle,
) -> ViewHandle {
unsafe {
let style_meta = abi::metadata0(resolve::siwa_style_ma());
assert!(!style_meta.is_null(), "SignInWithAppleButton.Style metadata");
let getter = resolve::sym_in(resolve::AUTH_SWIFTUI, style.getter());
assert!(!getter.is_null(), "{:?}", style.getter());
let mut value = static_getter_indirect(getter, style_meta, 0);
let (meta, wt, size) =
super::opaque_result_type(resolve::siwa_style_oret(), &[view.meta, view.wt]);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::siwa_style_fn(),
in("x0") value.as_mut_ptr(),
in("x1") view.meta,
in("x2") view.wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
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"),
);
wrap_owned(result, meta, wt)
}
}