use super::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::swift_type;
pub(crate) fn action_words(action: impl Fn() + 'static) -> [u64; 2] {
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(invoke_action, boxed_ctx(Box::new(action) as Box<dyn Fn()>))))
as *mut abi::ClosureCtx;
[abi::swift_closure_thunk as *const () as u64, ctx as u64]
}
pub(crate) fn boxed_action_words(action: Box<dyn Fn()>) -> [u64; 2] {
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
super::controls::run_boxed_action,
super::boxed_ctx(action),
))) as *mut abi::ClosureCtx;
[abi::swift_closure_thunk as *const () as u64, ctx as u64]
}
pub(crate) fn view_words(view: &ViewHandle) -> [u64; 2] {
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
emit_tuple,
Child::leak(view, Content::Concrete),
))) as *mut abi::ClosureCtx;
[abi::swift_closure_thunk as *const () as u64, ctx as u64]
}
pub(crate) fn wide_words(
body: unsafe extern "C" fn(u64, u64, u64, *mut c_void),
payload: *mut c_void,
) -> [u64; 2] {
let ctx = Box::leak(Box::new(abi::WideClosureCtx::new(body, payload)))
as *mut abi::WideClosureCtx;
[abi::wide_closure_thunk as *const () as u64, ctx as u64]
}
pub(crate) fn pair_words(
body: unsafe extern "C" fn(*const u8, *const u8, *mut c_void),
payload: *mut c_void,
) -> [u64; 2] {
let ctx = Box::leak(Box::new(abi::PairClosureCtx::new(body, payload)))
as *mut abi::PairClosureCtx;
[abi::pair_closure_thunk as *const () as u64, ctx as u64]
}
pub(crate) struct TagCallback {
pub(crate) f: Box<dyn Fn(i64)>,
pub(crate) meta: *const c_void,
pub(crate) indirect: bool,
}
pub(crate) unsafe extern "C" fn invoke_tag(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
unsafe {
let cb = &*(payload as *const TagCallback);
let tag = if cb.indirect {
abi::read_enum_tag(a0 as *const u8, cb.meta)
} else {
abi::read_enum_tag(a0.to_le_bytes().as_ptr(), cb.meta)
};
(cb.f)(i64::from(tag));
}
}
pub(crate) unsafe fn tag_words(f: impl Fn(i64) + 'static, meta: *const c_void, indirect: bool) -> [u64; 2] {
let cb = Box::leak(Box::new(TagCallback { f: Box::new(f), meta, indirect }));
wide_words(invoke_tag, cb as *mut TagCallback as *mut c_void)
}
pub(crate) unsafe extern "C" fn invoke_bool(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
let f = unsafe { boxed_ref::<dyn Fn(bool)>(payload) };
f(a0 & 1 != 0);
}
pub(crate) fn bool_words(f: impl Fn(bool) + 'static) -> [u64; 2] {
wide_words(invoke_bool, boxed_ctx(Box::new(f) as Box<dyn Fn(bool)>))
}
pub(crate) unsafe extern "C" fn invoke_url(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
unsafe {
let f = boxed_ref::<dyn Fn(&str)>(payload);
let (s0, s1): (u64, u64);
core::arch::asm!("blr {f}", f = in(reg) resolve::url_absolute_string(),
in("x20") a0,
lateout("x0") s0, lateout("x1") s1,
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("x21") _, clobber_abi("C"),
);
let mut raw = [0u8; 16];
raw[..8].copy_from_slice(&s0.to_le_bytes());
raw[8..].copy_from_slice(&s1.to_le_bytes());
f(&abi::read_swift_string(raw));
}
}
pub(crate) fn url_words(f: impl Fn(&str) + 'static) -> [u64; 2] {
wide_words(invoke_url, boxed_ctx(Box::new(f) as Box<dyn Fn(&str)>))
}
pub(crate) struct TagPairCallback {
pub(crate) f: Box<dyn Fn(i64, i64)>,
pub(crate) meta: *const c_void,
}
pub(crate) unsafe extern "C" fn invoke_tag_pair(a: *const u8, b: *const u8, payload: *mut c_void) {
unsafe {
let cb = &*(payload as *const TagPairCallback);
let ta = abi::read_enum_tag((a as u64).to_le_bytes().as_ptr(), cb.meta);
let tb = abi::read_enum_tag((b as u64).to_le_bytes().as_ptr(), cb.meta);
(cb.f)(i64::from(ta), i64::from(tb));
}
}
pub(crate) unsafe extern "C" fn invoke_word_pair(a: *const u8, b: *const u8, payload: *mut c_void) {
let f = unsafe { boxed_ref::<dyn Fn(u64, u64)>(payload) };
f(a as u64, b as u64);
}
pub(crate) unsafe fn generic1_modifier_ints(
func: *const c_void,
oret: *const c_void,
view: &ViewHandle,
arg_meta: *const c_void,
arg_wt: *const c_void,
leading: &[u64],
floats: [f64; 4],
) -> ViewHandle {
unsafe {
let subst = [view.meta, arg_meta, view.wt, arg_wt];
let (meta, wt, size) = opaque_result_type(oret, &subst);
let mut ints = Vec::with_capacity(leading.len() + 4);
ints.extend_from_slice(leading);
ints.push(view.meta as u64);
ints.push(arg_meta as u64);
ints.push(view.wt as u64);
ints.push(arg_wt as u64);
let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, floats, size);
wrap_owned(r, meta, wt)
}
}
pub(crate) unsafe fn content_modifier(
func: *const c_void,
oret: *const c_void,
view: &ViewHandle,
leading: &[u64],
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let mut ints = leading.to_vec();
ints.extend_from_slice(&view_words(content));
generic1_modifier_ints(
func,
oret,
view,
content.meta,
content.wt,
&ints,
[0.0; 4],
)
}
}
pub(crate) unsafe fn frozen_static(meta: *const c_void, getter: &'static core::ffi::CStr) -> u64 {
unsafe {
let f = resolve::sym(getter);
assert!(!f.is_null(), "missing static: {}", crate::diag::symbol_name(getter));
abi::static_regs(f, meta)[0]
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ActionKind {
Default,
Escape,
#[cfg(not(target_os = "macos"))]
MagicTap,
ShowMenu,
Delete,
}
impl ActionKind {
pub(crate) fn getter(self) -> &'static core::ffi::CStr {
match self {
Self::Default => c"$s7SwiftUI23AccessibilityActionKindV7defaultACvgZ",
Self::Escape => c"$s7SwiftUI23AccessibilityActionKindV6escapeACvgZ",
#[cfg(not(target_os = "macos"))]
Self::MagicTap => c"$s7SwiftUI23AccessibilityActionKindV8magicTapACvgZ",
Self::ShowMenu => c"$s7SwiftUI23AccessibilityActionKindV8showMenuACvgZ",
Self::Delete => c"$s7SwiftUI23AccessibilityActionKindV6deleteACvgZ",
}
}
}
swift_statics! {
pub enum SubmitTrigger {
Text = c"$s7SwiftUI14SubmitTriggersV4textACvgZ",
Search = c"$s7SwiftUI14SubmitTriggersV6searchACvgZ",
Collection = c"$s7SwiftUI14SubmitTriggersV10collectionACvgZ",
}
}
swift_statics! {
pub enum EventModifier {
Shift = c"$s7SwiftUI14EventModifiersV5shiftACvgZ",
Control = c"$s7SwiftUI14EventModifiersV7controlACvgZ",
Option = c"$s7SwiftUI14EventModifiersV6optionACvgZ",
Command = c"$s7SwiftUI14EventModifiersV7commandACvgZ",
CapsLock = c"$s7SwiftUI14EventModifiersV8capsLockACvgZ",
Function = c"$s7SwiftUI14EventModifiersV8functionACvgZ",
NumericPad = c"$s7SwiftUI14EventModifiersV10numericPadACvgZ",
All = c"$s7SwiftUI14EventModifiersV3allACvgZ",
}
}
pub fn accessibility_action(view: &ViewHandle, kind: ActionKind, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::a11y_action_kind_ma());
let k = resilient_static(meta, kind.getter());
let c = action_words(action);
a11y_modifier(resolve::a11y_action_fn(), view, &[k.as_ptr() as u64, c[0], c[1]], [0.0; 4])
}
}
pub fn accessibility_adjustable_action(view: &ViewHandle, action: impl Fn(i64) + 'static) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::a11y_adjustment_direction_ma());
let c = tag_words(action, meta, true);
a11y_modifier(resolve::a11y_adjustable_action_fn(), view, &c, [0.0; 4])
}
}
pub fn accessibility_scroll_action(view: &ViewHandle, action: impl Fn(i64) + 'static) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::edge_enum_ma());
let c = tag_words(action, meta, false);
a11y_modifier(resolve::a11y_scroll_action_fn(), view, &c, [0.0; 4])
}
}
pub fn rename_action(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::rename_action_fn(),
resolve::rename_action_oret(),
view,
&action_words(action),
[0.0; 4],
)
}
}
pub fn on_submit(view: &ViewHandle, trigger: SubmitTrigger, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::submit_triggers_ma());
let t = resilient_static(meta, trigger.getter());
let c = action_words(action);
opaque_modifier(
resolve::on_submit_fn(),
resolve::on_submit_oret(),
view,
&[t.as_ptr() as u64, c[0], c[1]],
[0.0; 4],
)
}
}
pub fn on_delete_command(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_delete_command_fn(),
resolve::on_delete_command_oret(),
view,
&action_words(action),
[0.0; 4],
)
}
}
pub fn on_exit_command(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_exit_command_fn(),
resolve::on_exit_command_oret(),
view,
&action_words(action),
[0.0; 4],
)
}
}
pub fn on_move_command(view: &ViewHandle, action: impl Fn(i64) + 'static) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::move_command_direction_ma());
let c = tag_words(action, meta, true);
opaque_modifier(
resolve::on_move_command_fn(),
resolve::on_move_command_oret(),
view,
&c,
[0.0; 4],
)
}
}
pub fn on_interactive_resize_change(view: &ViewHandle, action: impl Fn(bool) + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_interactive_resize_fn(),
resolve::on_interactive_resize_oret(),
view,
&bool_words(action),
[0.0; 4],
)
}
}
pub fn on_scroll_visibility_change(
view: &ViewHandle,
threshold: f64,
action: impl Fn(bool) + 'static,
) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_scroll_visibility_fn(),
resolve::on_scroll_visibility_oret(),
view,
&bool_words(action),
[threshold, 0.0, 0.0, 0.0],
)
}
}
pub fn on_open_url(view: &ViewHandle, action: impl Fn(&str) + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_open_url_fn(),
resolve::on_open_url_oret(),
view,
&url_words(action),
[0.0; 4],
)
}
}
pub fn on_scroll_phase_change(view: &ViewHandle, action: impl Fn(i64, i64) + 'static) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::scroll_phase_ma());
let cb = Box::leak(Box::new(TagPairCallback { f: Box::new(action), meta }));
let c = pair_words(invoke_tag_pair, cb as *mut TagPairCallback as *mut c_void);
opaque_modifier(
resolve::on_scroll_phase_change_fn(),
resolve::on_scroll_phase_change_oret(),
view,
&c,
[0.0; 4],
)
}
}
#[derive(Clone, Copy, Debug)]
pub struct TransactionRef(*mut c_void);
impl TransactionRef {
pub fn set_disables_animations(self, value: bool) {
unsafe {
abi::call_out(
resolve::transaction_disables_animations_set(),
abi::ints(&[u64::from(value)]),
[0.0; 4],
self.0 as u64,
core::ptr::null_mut(),
);
}
}
}
pub(crate) unsafe extern "C" fn invoke_transaction(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
let f = unsafe { boxed_ref::<dyn Fn(TransactionRef)>(payload) };
f(TransactionRef(a0 as *mut c_void));
}
pub fn transaction(view: &ViewHandle, action: impl Fn(TransactionRef) + 'static) -> ViewHandle {
unsafe {
let c = wide_words(invoke_transaction, boxed_ctx(Box::new(action) as Box<dyn Fn(TransactionRef)>));
opaque_modifier(resolve::transaction_fn(), resolve::transaction_oret(), view, &c, [0.0; 4])
}
}
pub fn accessibility_children(view: &ViewHandle, children: &ViewHandle) -> ViewHandle {
unsafe {
content_modifier(
resolve::a11y_children_fn(),
resolve::a11y_children_oret(),
view,
&[],
children,
)
}
}
pub fn accessibility_representation(view: &ViewHandle, representation: &ViewHandle) -> ViewHandle {
unsafe {
content_modifier(
resolve::a11y_representation_fn(),
resolve::a11y_representation_oret(),
view,
&[],
representation,
)
}
}
swift_statics! {
pub enum ActionCategory {
Default = c"$s7SwiftUI27AccessibilityActionCategoryV7defaultACvgZ",
Edit = c"$s7SwiftUI27AccessibilityActionCategoryV4editACvgZ",
}
}
pub fn accessibility_actions(
view: &ViewHandle,
category: ActionCategory,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::a11y_action_category_ma());
let c = resilient_static(meta, category.getter());
content_modifier(
resolve::a11y_actions_fn(),
resolve::a11y_actions_oret(),
view,
&[c.as_ptr() as u64],
content,
)
}
}
pub fn toolbar_title_menu(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
unsafe {
content_modifier(
resolve::toolbar_title_menu_fn(),
resolve::toolbar_title_menu_oret(),
view,
&[],
content,
)
}
}
pub fn section_actions(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
unsafe {
content_modifier(
resolve::section_actions_fn(),
resolve::section_actions_oret(),
view,
&[],
content,
)
}
}
pub fn tab_item(view: &ViewHandle, label: &ViewHandle) -> ViewHandle {
unsafe {
content_modifier(resolve::tab_item_fn(), resolve::tab_item_oret(), view, &[], label)
}
}
#[cfg(test)]
mod closure_tests {
use super::*;
use std::sync::atomic::{AtomicI64, Ordering};
unsafe fn call_closure(words: [u64; 2], arg: u64) {
unsafe {
core::arch::asm!("blr {f}", f = in(reg) words[0],
in("x0") arg, in("x20") words[1],
lateout("x1") _, 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("x21") _, clobber_abi("C"),
);
}
}
static SEEN: AtomicI64 = AtomicI64::new(-1);
fn record(v: i64) {
SEEN.store(v, Ordering::SeqCst);
}
#[test]
fn frozen_enum_case_arrives_in_the_register() {
unsafe {
let meta = abi::metadata0(resolve::edge_enum_ma());
let words = tag_words(record, meta, false);
for case in 0..4u64 {
SEEN.store(-1, Ordering::SeqCst);
call_closure(words, case);
assert_eq!(SEEN.load(Ordering::SeqCst), case as i64, "Edge case {case}");
}
}
}
#[test]
fn resilient_enum_case_arrives_by_address() {
unsafe {
let meta = abi::metadata0(resolve::move_command_direction_ma());
let words = tag_words(record, meta, true);
for case in 0..4u32 {
let mut buf = vec![0u8; abi::type_bytes(meta)];
abi::inject_enum_tag(&mut buf, meta, case);
SEEN.store(-1, Ordering::SeqCst);
call_closure(words, buf.as_ptr() as u64);
assert_eq!(SEEN.load(Ordering::SeqCst), i64::from(case), "direction {case}");
}
}
}
fn record_bool(v: bool) {
SEEN.store(i64::from(v), Ordering::SeqCst);
}
#[test]
fn bool_closure_reads_the_low_bit() {
let words = bool_words(record_bool);
unsafe {
SEEN.store(-1, Ordering::SeqCst);
call_closure(words, 1);
assert_eq!(SEEN.load(Ordering::SeqCst), 1);
call_closure(words, 0);
assert_eq!(SEEN.load(Ordering::SeqCst), 0);
}
}
#[test]
fn enum_tag_round_trips() {
unsafe {
let meta = abi::metadata0(resolve::scroll_phase_ma());
let mut buf = vec![0u8; abi::type_bytes(meta)];
for case in 0..5u32 {
abi::inject_enum_tag(&mut buf, meta, case);
assert_eq!(abi::read_enum_tag(buf.as_ptr(), meta), case);
}
}
}
#[test]
fn closure_context_outlives_construction() {
let words = {
let view = text("gone by now");
view_words(&view)
};
assert_ne!(words[0], 0);
assert_ne!(words[1], 0);
let mut out = 0u64;
unsafe {
core::arch::asm!("blr {f}", f = in(reg) words[0],
in("x8") &mut out as *mut u64, in("x20") words[1],
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("x21") _, clobber_abi("C"),
);
}
assert_ne!(out, 0, "the ViewBuilder closure handed back nothing");
}
}
pub(crate) fn ignoring_value_words(action: impl Fn() + 'static) -> [u64; 2] {
wide_words(invoke_ignoring_value, boxed_ctx(Box::new(action) as Box<dyn Fn()>))
}
pub(crate) unsafe extern "C" fn invoke_ignoring_value(
_a0: u64,
_a1: u64,
_a2: u64,
payload: *mut c_void,
) {
let f = unsafe { boxed_ref::<dyn Fn()>(payload) };
f();
}
pub fn accessibility_zoom_action(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe { a11y_modifier(resolve::a11y_zoom_action_fn(), view, &ignoring_value_words(action), [0.0; 4]) }
}
pub fn on_continue_user_activity(view: &ViewHandle, kind: &str, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
let s = abi::swift_string(kind);
let c = ignoring_value_words(action);
opaque_modifier(
resolve::on_continue_user_activity_fn(),
resolve::on_continue_user_activity_oret(),
view,
&[
u64::from_le_bytes(s[..8].try_into().expect("lo")),
u64::from_le_bytes(s[8..].try_into().expect("hi")),
c[0],
c[1],
],
[0.0; 4],
)
}
}
pub fn presentation_background(
view: &ViewHandle,
alignment: Alignment,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let a = alignment.words();
content_modifier(
resolve::presentation_background_fn(),
resolve::presentation_background_oret(),
view,
&[a[0], a[1]],
content,
)
}
}
swift_statics! {
pub enum ContentType {
PlainText = c"$s22UniformTypeIdentifiers6UTTypeV9plainTextACvgZ",
Text = c"$s22UniformTypeIdentifiers6UTTypeV4textACvgZ",
Url = c"$s22UniformTypeIdentifiers6UTTypeV3urlACvgZ",
}
}
pub(crate) fn word_closure(
body: unsafe extern "C" fn(*mut c_void, u64) -> u64,
payload: *mut c_void,
) -> [u64; 2] {
let ctx = Box::leak(Box::new(abi::WordClosureCtx::new(body, payload)))
as *mut abi::WordClosureCtx;
[abi::word_closure_thunk as *const () as u64, ctx as u64]
}
unsafe impl Send for ItemProvider {}
unsafe impl Sync for ItemProvider {}
pub(crate) unsafe fn provider_array(items: &[&str]) -> u64 {
unsafe {
let meta = crate::type_by_name!(swift_type!(objc NSItemProvider).as_bytes());
let stride = abi::type_bytes(meta);
let mut bytes = vec![0u8; stride * items.len()];
for (i, t) in items.iter().enumerate() {
let p = ItemProvider::plain_text(t);
bytes[i * stride..i * stride + 8].copy_from_slice(&(p.0 as u64).to_le_bytes());
}
abi::swift_array(meta, items.len(), &bytes)
}
}
pub(crate) type DropDecide = Box<dyn Fn(&[ItemProvider]) -> bool>;
pub(crate) type DropObserve = Box<dyn Fn(&[ItemProvider])>;
pub(crate) struct ProviderPayload {
array: u64,
decide: Option<DropDecide>,
observe: Option<DropObserve>,
}
pub(crate) unsafe extern "C" fn emit_providers(payload: *mut c_void, _arg: u64) -> u64 {
unsafe {
let p = &*(payload as *const ProviderPayload);
swift_runtime_sys::RuntimeRaw::swift_retain(p.array as *mut c_void);
p.array
}
}
pub(crate) unsafe extern "C" fn emit_first_provider(payload: *mut c_void, _arg: u64) -> u64 {
unsafe {
let p = &*(payload as *const ProviderPayload);
let items = read_provider_array(p.array);
items.first().map_or(0, |i| i.0 as u64)
}
}
pub(crate) unsafe extern "C" fn decide_on_providers(payload: *mut c_void, arg: u64) -> u64 {
unsafe {
let p = &*(payload as *const ProviderPayload);
let items = read_provider_array(arg);
u64::from(p.decide.as_ref().map(|f| f(&items)).unwrap_or(false))
}
}
pub(crate) unsafe extern "C" fn observe_providers(
a0: u64,
_a1: u64,
_a2: u64,
payload: *mut c_void,
) {
unsafe {
let p = &*(payload as *const ProviderPayload);
if let Some(f) = p.observe.as_ref() {
f(&read_provider_array(a0));
}
}
}
pub(crate) fn provider_payload(
array: u64,
decide: Option<DropDecide>,
observe: Option<DropObserve>,
) -> *mut c_void {
Box::leak(Box::new(ProviderPayload { array, decide, observe })) as *mut ProviderPayload
as *mut c_void
}
swift_statics! {
pub enum ContainerBackgroundPlacement {
Window = c"$s7SwiftUI28ContainerBackgroundPlacementV6windowACvgZ",
#[cfg(not(target_os = "macos"))]
Navigation = c"$s7SwiftUI28ContainerBackgroundPlacementV10navigationACvgZ",
Presentation = c"$s7SwiftUI28ContainerBackgroundPlacementV12presentationACvgZ",
#[cfg(not(target_os = "macos"))]
NavigationSplitView = c"$s7SwiftUI28ContainerBackgroundPlacementV19navigationSplitViewACvgZ",
}
}
swift_cases! {
pub enum VerticalEdge {
Top = 0,
Bottom = 1,
}
}
pub fn container_background(
view: &ViewHandle,
placement: ContainerBackgroundPlacement,
alignment: Alignment,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::container_background_placement_ma());
let p = resilient_static(meta, placement.getter());
let a = alignment.words();
content_modifier(
resolve::container_background_fn(),
resolve::container_background_oret(),
view,
&[p.as_ptr() as u64, a[0], a[1]],
content,
)
}
}
unsafe fn safe_area_args(
edge: VerticalEdge,
alignment: HorizontalAlignment,
spacing: Option<f64>,
) -> Vec<u64> {
unsafe {
let e = abi::enum_argument(abi::metadata0(resolve::vertical_edge_ma()), edge.tag());
let a = frozen_static(abi::metadata0(resolve::horizontal_alignment_ma()), alignment.getter());
let mut ints = vec![e, a];
ints.extend(opt_f64_words(spacing));
ints
}
}
pub fn safe_area_inset(
view: &ViewHandle,
edge: VerticalEdge,
alignment: HorizontalAlignment,
spacing: Option<f64>,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let args = safe_area_args(edge, alignment, spacing);
content_modifier(
resolve::safe_area_inset_fn(),
resolve::safe_area_inset_oret(),
view,
&args,
content,
)
}
}
pub fn safe_area_bar(
view: &ViewHandle,
edge: VerticalEdge,
alignment: HorizontalAlignment,
spacing: Option<f64>,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let args = safe_area_args(edge, alignment, spacing);
content_modifier(
resolve::safe_area_bar_fn(),
resolve::safe_area_bar_oret(),
view,
&args,
content,
)
}
}
pub fn user_activity(view: &ViewHandle, kind: &str, active: bool, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
let s = abi::swift_string(kind);
let c = ignoring_value_words(action);
opaque_modifier(
resolve::user_activity_fn(),
resolve::user_activity_oret(),
view,
&[
u64::from_le_bytes(s[..8].try_into().expect("lo")),
u64::from_le_bytes(s[8..].try_into().expect("hi")),
u64::from(active),
c[0],
c[1],
],
[0.0; 4],
)
}
}
swift_statics! {
pub enum SensoryFeedback {
Success = c"$s7SwiftUI15SensoryFeedbackV7successACvgZ",
Warning = c"$s7SwiftUI15SensoryFeedbackV7warningACvgZ",
Error = c"$s7SwiftUI15SensoryFeedbackV5errorACvgZ",
Selection = c"$s7SwiftUI15SensoryFeedbackV9selectionACvgZ",
Impact = c"$s7SwiftUI15SensoryFeedbackV6impactACvgZ",
Alignment = c"$s7SwiftUI15SensoryFeedbackV9alignmentACvgZ",
Decrease = c"$s7SwiftUI15SensoryFeedbackV8decreaseACvgZ",
Increase = c"$s7SwiftUI15SensoryFeedbackV8increaseACvgZ",
LevelChange = c"$s7SwiftUI15SensoryFeedbackV11levelChangeACvgZ",
PathComplete = c"$s7SwiftUI15SensoryFeedbackV12pathCompleteACvgZ",
Start = c"$s7SwiftUI15SensoryFeedbackV5startACvgZ",
Stop = c"$s7SwiftUI15SensoryFeedbackV4stopACvgZ",
}
}
pub(crate) fn words(bytes: &[u8]) -> Vec<u64> {
bytes
.chunks(8)
.map(|c| {
let mut w = [0u8; 8];
w[..c.len()].copy_from_slice(c);
u64::from_le_bytes(w)
})
.collect()
}
swift_statics! {
pub enum SearchFieldPlacement {
Automatic = c"$s7SwiftUI20SearchFieldPlacementV9automaticACvgZ",
ToolbarPrincipal = c"$s7SwiftUI20SearchFieldPlacementV16toolbarPrincipalACvgZ",
NavigationColumn = c"$s7SwiftUI20SearchFieldPlacementV16navigationColumnACvgZ",
#[cfg(not(target_os = "macos"))]
NavigationBarDrawer = c"$s7SwiftUI20SearchFieldPlacementV19navigationBarDrawerACvgZ",
Sidebar = c"$s7SwiftUI20SearchFieldPlacementV7sidebarACvgZ",
Toolbar = c"$s7SwiftUI20SearchFieldPlacementV7toolbarACvgZ",
}
}
pub(crate) unsafe fn two_view_modifier(
func: *const c_void,
oret: *const c_void,
view: &ViewHandle,
leading: &[u64],
a: &ViewHandle,
b: &ViewHandle,
) -> ViewHandle {
unsafe {
let subst = [view.meta, a.meta, b.meta, view.wt, a.wt, b.wt];
let (meta, wt, size) = opaque_result_type(oret, &subst);
let mut ints = leading.to_vec();
ints.extend_from_slice(&view_words(a));
ints.extend_from_slice(&view_words(b));
ints.extend_from_slice(&[view.meta as u64, a.meta as u64, b.meta as u64]);
ints.extend_from_slice(&[view.wt as u64, a.wt as u64, b.wt as u64]);
let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, [0.0; 4], size);
wrap_owned(r, meta, wt)
}
}
swift_statics! {
pub enum SearchSuggestionsPlacementSet {
Menu = c"$s7SwiftUI26SearchSuggestionsPlacementV3SetV4menuAEvgZ",
Content = c"$s7SwiftUI26SearchSuggestionsPlacementV3SetV7contentAEvgZ",
}
}
swift_statics! {
pub enum TextInputFormattingControlPlacementSet {
All = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV3allAEvgZ",
Default = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV7defaultAEvgZ",
ContextMenu = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV11contextMenuAEvgZ",
AccessoryBar = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV12accessoryBarAEvgZ",
#[cfg(not(target_os = "macos"))]
InputAssistant = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV14inputAssistantAEvgZ",
FontPanel = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV9fontPanelAEvgZ",
}
}
swift_statics! {
pub enum CharacterSetKind {
Alphanumerics = c"$s10Foundation12CharacterSetV13alphanumericsACvgZ",
Letters = c"$s10Foundation12CharacterSetV7lettersACvgZ",
Decimals = c"$s10Foundation12CharacterSetV13decimalDigitsACvgZ",
Whitespaces = c"$s10Foundation12CharacterSetV11whitespacesACvgZ",
}
}
swift_statics! {
pub enum KeyPressPhases {
Down = c"$s7SwiftUI8KeyPressV6PhasesV4downAEvgZ",
Up = c"$s7SwiftUI8KeyPressV6PhasesV2upAEvgZ",
Repeat = c"$s7SwiftUI8KeyPressV6PhasesV6repeatAEvgZ",
All = c"$s7SwiftUI8KeyPressV6PhasesV3allAEvgZ",
}
}
pub fn navigation_transition(view: &ViewHandle) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::automatic_navigation_transition_ma());
let wt = abi::get_view_wt_for_proto(meta, resolve::navigation_transition_proto());
assert!(!wt.is_null(), "AutomaticNavigationTransition: NavigationTransition");
let value = abi::static_out(
resolve::navigation_transition_automatic(), meta, abi::type_bytes(meta));
generic1_modifier_ints(
resolve::navigation_transition_fn(),
resolve::navigation_transition_oret(),
view,
meta,
wt,
&[value.as_ptr() as u64],
[0.0; 4],
)
}
}
swift_statics! {
pub enum LineHeightKind {
Variable = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV8variableAFvgZ",
Normal = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV6normalAFvgZ",
Tight = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV5tightAFvgZ",
Loose = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV5looseAFvgZ",
}
}
pub fn line_height(view: &ViewHandle, height: Option<LineHeightKind>) -> ViewHandle {
unsafe {
let payload = abi::metadata0(resolve::line_height_ma());
let opt = abi::resolve_type_by_mangled_name(
b"10Foundation16AttributedStringV8CoreTextE10LineHeightVSg");
assert!(!opt.is_null(), "Optional<AttributedString.LineHeight>");
let mut buf = vec![0u8; abi::type_bytes(opt)];
if let Some(kind) = height {
let v = resilient_static(payload, kind.getter());
buf[..v.len()].copy_from_slice(&v);
}
abi::store_optional_tag(&mut buf, payload, height.is_some());
opaque_modifier(
resolve::line_height_fn(),
resolve::line_height_oret(),
view,
&[buf.as_ptr() as u64],
[0.0; 4],
)
}
}
pub fn refreshable(view: &ViewHandle, action: impl Fn() + Send + Sync + 'static) -> ViewHandle {
unsafe {
static mut RECORD: crate::asyncfn::FunctionPointer =
crate::asyncfn::FunctionPointer::new();
let closure = crate::asyncfn::prepare(
crate::static_mut(&raw mut RECORD),
0,
action,
);
opaque_modifier(
resolve::refreshable_fn(),
resolve::refreshable_oret(),
view,
&closure,
[0.0; 4],
)
}
}
#[doc(hidden)]
pub unsafe fn generic1_modifier_ints_for_test(
func: *const c_void,
oret: *const c_void,
view: &ViewHandle,
arg_meta: *const c_void,
arg_wt: *const c_void,
leading: &[u64],
) -> ViewHandle {
unsafe { generic1_modifier_ints(func, oret, view, arg_meta, arg_wt, leading, [0.0; 4]) }
}
swift_statics! {
pub enum TaskPriority {
High = c"$sScP4highScPvgZ",
UserInitiated = c"$sScP13userInitiatedScPvgZ",
Utility = c"$sScP7utilityScPvgZ",
Low = c"$sScP3lowScPvgZ",
Background = c"$sScP10backgroundScPvgZ",
}
}
pub fn task(
view: &ViewHandle,
id: i64,
priority: TaskPriority,
file: &str,
line: i64,
action: impl Fn() + Send + Sync + 'static,
) -> ViewHandle {
unsafe {
static mut RECORD: crate::asyncfn::FunctionPointer =
crate::asyncfn::FunctionPointer::new();
let closure = crate::asyncfn::prepare(
crate::static_mut(&raw mut RECORD),
1,
action,
);
let ctx = Box::leak(Box::new(abi::IsolatedContext::nonisolated()))
as *mut abi::IsolatedContext;
let pm = abi::metadata0(resolve::task_priority_ma());
let p = resilient_static(pm, priority.getter());
let name = abi::swift_string(&format!("View.task @ {file}:{line}"));
let n0 = u64::from_le_bytes(name[..8].try_into().expect("name lo"));
let n1 = u64::from_le_bytes(name[8..].try_into().expect("name hi"));
let meta = resolve::int_meta();
let wt = resolve::int_equatable_wt();
let modifier = abi::generic_metadata2(resolve::task_modifier_ma(), meta, wt);
assert!(!modifier.is_null(), "_TaskValueModifier2<Int> metadata");
let mut value = vec![0u8; abi::type_bytes(modifier)];
let mut boxed = id;
core::arch::asm!(
"sub sp, sp, #16",
"stp {s0}, {s1}, [sp]",
"blr {f}",
"add sp, sp, #16",
f = in(reg) resolve::task_modifier_init(),
s0 = in(reg) meta, s1 = in(reg) wt,
in("x0") &mut boxed as *mut i64,
in("x1") n0, in("x2") n1,
in("x3") 0u64, in("x4") 0u64,
in("x5") p.as_ptr(),
in("x6") closure[0], in("x7") ctx as u64,
in("x8") value.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"),
);
apply_modifier(view, modifier, &value)
}
}
pub fn task_once(
view: &ViewHandle,
priority: TaskPriority,
file: &str,
line: i64,
action: impl Fn() + Send + Sync + 'static,
) -> ViewHandle {
unsafe {
static mut RECORD: crate::asyncfn::FunctionPointer =
crate::asyncfn::FunctionPointer::new();
let closure = crate::asyncfn::prepare(
crate::static_mut(&raw mut RECORD),
2,
action,
);
let ctx = Box::leak(Box::new(abi::IsolatedContext::nonisolated()))
as *mut abi::IsolatedContext;
let pm = abi::metadata0(resolve::task_priority_ma());
let p = resilient_static(pm, priority.getter());
let name = abi::swift_string(&format!("View.task @ {file}:{line}"));
let n0 = u64::from_le_bytes(name[..8].try_into().expect("name lo"));
let n1 = u64::from_le_bytes(name[8..].try_into().expect("name hi"));
let modifier = abi::metadata0(resolve::task_modifier2_ma());
assert!(!modifier.is_null(), "_TaskModifier2 metadata");
let mut value = vec![0u8; abi::type_bytes(modifier)];
core::arch::asm!("blr {f}", f = in(reg) resolve::task_modifier2_init(),
in("x0") n0, in("x1") n1,
in("x2") 0u64, in("x3") 0u64,
in("x4") p.as_ptr(),
in("x5") closure[0], in("x6") ctx as u64,
in("x8") value.as_mut_ptr(),
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"),
);
apply_modifier(view, modifier, &value)
}
}