use super::{wrap_owned, ViewHandle};
use crate::abi;
use crate::conformance::{synthesize, Conformance, Protocol, Witness};
use crate::resolve;
use core::ffi::c_void;
use std::sync::{Mutex, OnceLock};
pub struct ButtonConfiguration<'a> {
raw: *mut c_void,
marker: core::marker::PhantomData<&'a ()>,
}
impl ButtonConfiguration<'_> {
pub fn label(&self) -> ViewHandle {
unsafe { config_view(resolve::button_config_label(), resolve::button_config_label_ma(), self.raw) }
}
pub fn is_pressed(&self) -> bool {
unsafe { config_flag(resolve::button_config_pressed(), self.raw) }
}
}
pub struct LabelConfiguration<'a> {
raw: *mut c_void,
marker: core::marker::PhantomData<&'a ()>,
}
impl LabelConfiguration<'_> {
pub fn title(&self) -> ViewHandle {
unsafe { config_view(resolve::label_config_title(), resolve::label_config_title_ma(), self.raw) }
}
pub fn icon(&self) -> ViewHandle {
unsafe { config_view(resolve::label_config_icon(), resolve::label_config_icon_ma(), self.raw) }
}
}
unsafe fn config_view(
getter: *const c_void,
ma: *const c_void,
raw: *mut c_void,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(ma);
assert!(!meta.is_null(), "style configuration property metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let mut out = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) getter,
in("x8") out.as_mut_ptr(),
in("x20") raw,
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"),
);
wrap_owned(out, meta, wt)
}
}
unsafe fn config_flag(getter: *const c_void, raw: *mut c_void) -> bool {
unsafe {
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) getter,
in("x20") raw,
lateout("x0") out,
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"),
);
out & 1 != 0
}
}
pub trait ButtonStyling: Send + Sync + 'static {
fn make_body(&self, configuration: ButtonConfiguration<'_>) -> ViewHandle;
}
impl<F> ButtonStyling for F
where
F: Fn(ButtonConfiguration<'_>) -> ViewHandle + Send + Sync + 'static,
{
fn make_body(&self, configuration: ButtonConfiguration<'_>) -> ViewHandle {
self(configuration)
}
}
pub trait LabelStyling: Send + Sync + 'static {
fn make_body(&self, configuration: LabelConfiguration<'_>) -> ViewHandle;
}
impl<F> LabelStyling for F
where
F: Fn(LabelConfiguration<'_>) -> ViewHandle + Send + Sync + 'static,
{
fn make_body(&self, configuration: LabelConfiguration<'_>) -> ViewHandle {
self(configuration)
}
}
pub struct CustomButtonStyle {
index: i64,
}
pub struct CustomLabelStyle {
index: i64,
}
fn button_registry() -> &'static Mutex<Vec<&'static dyn ButtonStyling>> {
static R: OnceLock<Mutex<Vec<&'static dyn ButtonStyling>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(Vec::new()))
}
fn label_registry() -> &'static Mutex<Vec<&'static dyn LabelStyling>> {
static R: OnceLock<Mutex<Vec<&'static dyn LabelStyling>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn button_style_fn<S: ButtonStyling>(style: S) -> CustomButtonStyle {
let leaked: &'static dyn ButtonStyling = Box::leak(Box::new(style));
let mut r = button_registry().lock().expect("button style registry");
r.push(leaked);
CustomButtonStyle { index: r.len() as i64 - 1 }
}
pub fn label_style_fn<S: LabelStyling>(style: S) -> CustomLabelStyle {
let leaked: &'static dyn LabelStyling = Box::leak(Box::new(style));
let mut r = label_registry().lock().expect("label style registry");
r.push(leaked);
CustomLabelStyle { index: r.len() as i64 - 1 }
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_button_style_thunk",
"_rswift_button_style_thunk:",
"mov x2, x20",
"mov x1, x0",
"mov x0, x8",
"b _rswift_button_make_body",
".p2align 2",
".globl _rswift_label_style_thunk",
"_rswift_label_style_thunk:",
"mov x2, x20",
"mov x1, x0",
"mov x0, x8",
"b _rswift_label_make_body",
);
unsafe extern "C" {
#[link_name = "rswift_button_style_thunk"]
fn button_style_thunk();
#[link_name = "rswift_label_style_thunk"]
fn label_style_thunk();
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_button_make_body(
out: *mut u8,
configuration: *mut c_void,
this: *const u8,
) {
let index = unsafe { *(this as *const i64) } as usize;
let style = {
let r = button_registry().lock().expect("button style registry");
r.get(index).copied().expect("button style index out of range")
};
let body = style.make_body(ButtonConfiguration {
raw: configuration,
marker: core::marker::PhantomData,
});
unsafe { *(out as *mut u64) = body.into_anyview_word() };
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_label_make_body(
out: *mut u8,
configuration: *mut c_void,
this: *const u8,
) {
let index = unsafe { *(this as *const i64) } as usize;
let style = {
let r = label_registry().lock().expect("label style registry");
r.get(index).copied().expect("label style index out of range")
};
let body = style.make_body(LabelConfiguration {
raw: configuration,
marker: core::marker::PhantomData,
});
unsafe { *(out as *mut u64) = body.into_anyview_word() };
}
fn button_carrier() -> (*const c_void, *const c_void) {
static C: OnceLock<(usize, usize)> = OnceLock::new();
let (d, m) = *C.get_or_init(|| unsafe {
let (d, m) =
crate::state::emit_int_carrier(b"RustButtonStyle\0", b"6rswift15RustButtonStyleV\0");
(d as usize, m as usize)
});
(d as *const c_void, m as *const c_void)
}
fn label_carrier() -> (*const c_void, *const c_void) {
static C: OnceLock<(usize, usize)> = OnceLock::new();
let (d, m) = *C.get_or_init(|| unsafe {
let (d, m) =
crate::state::emit_int_carrier(b"RustLabelStyle\0", b"6rswift14RustLabelStyleV\0");
(d as usize, m as usize)
});
(d as *const c_void, m as *const c_void)
}
unsafe fn style_conformance(
protocol: &'static core::ffi::CStr,
descriptor: *const c_void,
make_body: *const c_void,
) -> Conformance {
unsafe {
let protocol = Protocol::from_symbol(protocol);
assert_eq!(protocol.requirement_count(), 3, "the style protocol changed shape");
synthesize(
&protocol,
descriptor,
&[
Witness::Metadata(resolve::anyview_wt()),
Witness::Metadata(resolve::anyview_meta()),
Witness::Function(make_body),
],
)
}
}
fn button_conformance() -> &'static Conformance {
static C: OnceLock<Conformance> = OnceLock::new();
C.get_or_init(|| unsafe {
style_conformance(
c"$s7SwiftUI11ButtonStyleMp",
button_carrier().0,
button_style_thunk as *const c_void,
)
})
}
fn label_conformance() -> &'static Conformance {
static C: OnceLock<Conformance> = OnceLock::new();
C.get_or_init(|| unsafe {
style_conformance(
c"$s7SwiftUI10LabelStyleMp",
label_carrier().0,
label_style_thunk as *const c_void,
)
})
}
unsafe fn apply_style(
view: &ViewHandle,
index: i64,
conformance: &Conformance,
carrier: (*const c_void, *const c_void),
f: *const c_void,
oret: *const c_void,
) -> ViewHandle {
unsafe {
let meta = carrier.1;
let mut value = vec![0u8; abi::type_bytes(meta)];
value[..8].copy_from_slice(&index.to_le_bytes());
let subst = [view.meta, meta, view.wt, conformance.witness_table()];
let (out_meta, out_wt, size) = super::opaque_result_type(oret, &subst);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) f,
in("x0") value.as_mut_ptr(),
in("x1") view.meta,
in("x2") meta,
in("x3") view.wt,
in("x4") conformance.witness_table(),
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, out_meta, out_wt)
}
}
pub fn custom_button_style(view: &ViewHandle, style: &CustomButtonStyle) -> ViewHandle {
unsafe {
apply_style(
view,
style.index,
button_conformance(),
button_carrier(),
resolve::button_style_custom_fn(),
resolve::button_style_custom_oret(),
)
}
}
pub fn custom_label_style(view: &ViewHandle, style: &CustomLabelStyle) -> ViewHandle {
unsafe {
apply_style(
view,
style.index,
label_conformance(),
label_carrier(),
resolve::label_style_fn(),
resolve::label_style_oret(),
)
}
}
pub trait Modifying: Send + Sync + 'static {
fn body(&self, content: ViewHandle) -> ViewHandle;
}
impl<F> Modifying for F
where
F: Fn(ViewHandle) -> ViewHandle + Send + Sync + 'static,
{
fn body(&self, content: ViewHandle) -> ViewHandle {
self(content)
}
}
pub struct CustomModifier {
index: i64,
}
fn modifier_registry() -> &'static Mutex<Vec<&'static dyn Modifying>> {
static R: OnceLock<Mutex<Vec<&'static dyn Modifying>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn modifier_fn<M: Modifying>(modifier: M) -> CustomModifier {
let leaked: &'static dyn Modifying = Box::leak(Box::new(modifier));
let mut r = modifier_registry().lock().expect("modifier registry");
r.push(leaked);
CustomModifier { index: r.len() as i64 - 1 }
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_view_modifier_thunk",
"_rswift_view_modifier_thunk:",
"mov x2, x20",
"mov x1, x0",
"mov x0, x8",
"b _rswift_view_modifier_body",
);
unsafe extern "C" {
#[link_name = "rswift_view_modifier_thunk"]
fn view_modifier_thunk();
}
static CONTENT_META: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
static CONTENT_WT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_view_modifier_body(
out: *mut u8,
content: *mut c_void,
this: *const u8,
) {
use std::sync::atomic::Ordering::Acquire;
let index = unsafe { *(this as *const i64) } as usize;
let modifier = {
let r = modifier_registry().lock().expect("modifier registry");
r.get(index).copied().expect("modifier index out of range")
};
let meta = CONTENT_META.load(Acquire) as *const c_void;
let wt = CONTENT_WT.load(Acquire) as *const c_void;
let handle = unsafe {
let mut bytes = vec![0u8; abi::type_bytes(meta)];
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(meta);
(vwt.initialize_with_copy)(bytes.as_mut_ptr() as *mut c_void, content, meta);
wrap_owned(bytes, meta, wt)
};
let body = modifier.body(handle);
unsafe { *(out as *mut u64) = body.into_anyview_word() };
}
fn modifier_carrier() -> (*const c_void, *const c_void) {
static C: OnceLock<(usize, usize)> = OnceLock::new();
let (d, m) = *C.get_or_init(|| unsafe {
let (d, m) =
crate::state::emit_int_carrier(b"RustModifier\0", b"6rswift12RustModifierV\0");
(d as usize, m as usize)
});
(d as *const c_void, m as *const c_void)
}
fn modifier_conformance() -> &'static Conformance {
static C: OnceLock<Conformance> = OnceLock::new();
C.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI12ViewModifierMp");
assert_eq!(
protocol.requirement_count(),
6,
"ViewModifier changed shape — it used to be Body, its conformance, \
three defaulted `_make…` entries and `body(content:)`",
);
let conformance = synthesize(
&protocol,
modifier_carrier().0,
&[
Witness::Metadata(resolve::anyview_wt()),
Witness::Metadata(resolve::anyview_meta()),
Witness::Default,
Witness::Default,
Witness::Default,
Witness::Function(view_modifier_thunk as *const c_void),
],
);
use std::sync::atomic::Ordering::Release;
let meta = abi::generic_metadata2(
resolve::vm_content_ma(),
modifier_carrier().1,
conformance.witness_table(),
);
assert!(!meta.is_null(), "_ViewModifier_Content<RustModifier> metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
CONTENT_META.store(meta as usize, Release);
CONTENT_WT.store(wt as usize, Release);
conformance
})
}
pub fn custom_modifier(view: &ViewHandle, modifier: &CustomModifier) -> ViewHandle {
unsafe {
let conformance = modifier_conformance();
let carrier = modifier_carrier().1;
let mut value = vec![0u8; abi::type_bytes(carrier)];
value[..8].copy_from_slice(&modifier.index.to_le_bytes());
let meta = abi::generic_metadata2(resolve::modified_content_ma(), view.meta, carrier);
assert!(!meta.is_null(), "ModifiedContent metadata");
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)];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::apply_view_modifier_fn(),
in("x0") value.as_mut_ptr(),
in("x1") view.meta,
in("x2") carrier,
in("x3") view.wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
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"),
);
let _ = conformance;
wrap_owned(result, meta, wt)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TransitionPhase {
WillAppear,
Identity,
DidDisappear,
}
impl TransitionPhase {
fn case(self) -> &'static str {
match self {
Self::WillAppear => "willAppear",
Self::Identity => "identity",
Self::DidDisappear => "didDisappear",
}
}
fn from_tag(tag: u8) -> Self {
unsafe {
let meta = abi::metadata0(resolve::transition_phase_ma());
assert!(!meta.is_null(), "TransitionPhase metadata");
for p in [Self::WillAppear, Self::Identity, Self::DidDisappear] {
let want = abi::enum_case_index(meta, p.case())
.unwrap_or_else(|| panic!("TransitionPhase has no `{}` case", p.case()));
if want as u8 == tag {
return p;
}
}
panic!("TransitionPhase tag {tag} is none of the three cases");
}
}
}
pub trait Transitioning: Send + Sync + 'static {
fn body(&self, content: ViewHandle, phase: TransitionPhase) -> ViewHandle;
}
impl<F> Transitioning for F
where
F: Fn(ViewHandle, TransitionPhase) -> ViewHandle + Send + Sync + 'static,
{
fn body(&self, content: ViewHandle, phase: TransitionPhase) -> ViewHandle {
self(content, phase)
}
}
pub struct CustomTransition {
index: i64,
}
fn transition_registry() -> &'static Mutex<Vec<&'static dyn Transitioning>> {
static R: OnceLock<Mutex<Vec<&'static dyn Transitioning>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn transition_fn<T: Transitioning>(transition: T) -> CustomTransition {
let leaked: &'static dyn Transitioning = Box::leak(Box::new(transition));
let mut r = transition_registry().lock().expect("transition registry");
r.push(leaked);
CustomTransition { index: r.len() as i64 - 1 }
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_transition_thunk",
"_rswift_transition_thunk:",
"mov x3, x20",
"mov x2, x1",
"mov x1, x0",
"mov x0, x8",
"b _rswift_transition_body",
);
unsafe extern "C" {
#[link_name = "rswift_transition_thunk"]
fn transition_thunk();
}
static PLACEHOLDER_META: std::sync::atomic::AtomicUsize =
std::sync::atomic::AtomicUsize::new(0);
static PLACEHOLDER_WT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_transition_body(
out: *mut u8,
content: *mut c_void,
phase: u64,
this: *const u8,
) {
use std::sync::atomic::Ordering::Acquire;
let index = unsafe { *(this as *const i64) } as usize;
let transition = {
let r = transition_registry().lock().expect("transition registry");
r.get(index).copied().expect("transition index out of range")
};
let meta = PLACEHOLDER_META.load(Acquire) as *const c_void;
let wt = PLACEHOLDER_WT.load(Acquire) as *const c_void;
let handle = unsafe {
let mut bytes = vec![0u8; abi::type_bytes(meta)];
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(meta);
(vwt.initialize_with_copy)(bytes.as_mut_ptr() as *mut c_void, content, meta);
wrap_owned(bytes, meta, wt)
};
let body = transition.body(handle, TransitionPhase::from_tag(phase as u8));
unsafe { *(out as *mut u64) = body.into_anyview_word() };
}
fn transition_carrier() -> (*const c_void, *const c_void) {
static C: OnceLock<(usize, usize)> = OnceLock::new();
let (d, m) = *C.get_or_init(|| unsafe {
let (d, m) =
crate::state::emit_int_carrier(b"RustTransition\0", b"6rswift14RustTransitionV\0");
(d as usize, m as usize)
});
(d as *const c_void, m as *const c_void)
}
fn transition_conformance() -> &'static Conformance {
static C: OnceLock<Conformance> = OnceLock::new();
C.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI10TransitionMp");
assert_eq!(
protocol.requirement_count(),
5,
"Transition changed shape — it used to be Body, its conformance, \
`body(content:phase:)` and two defaulted requirements",
);
let conformance = synthesize(
&protocol,
transition_carrier().0,
&[
Witness::Metadata(resolve::anyview_wt()),
Witness::Metadata(resolve::anyview_meta()),
Witness::Function(transition_thunk as *const c_void),
Witness::Default,
Witness::Default,
],
);
use std::sync::atomic::Ordering::Release;
let meta = abi::generic_metadata2(
resolve::placeholder_content_ma(),
transition_carrier().1,
conformance.witness_table(),
);
assert!(!meta.is_null(), "PlaceholderContentView<RustTransition> metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
PLACEHOLDER_META.store(meta as usize, Release);
PLACEHOLDER_WT.store(wt as usize, Release);
conformance
})
}
pub fn custom_transition(view: &ViewHandle, transition: &CustomTransition) -> ViewHandle {
unsafe {
let conformance = transition_conformance();
let carrier = transition_carrier().1;
let mut value = vec![0u8; abi::type_bytes(carrier)];
value[..8].copy_from_slice(&transition.index.to_le_bytes());
let boxed: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::any_transition_init(),
in("x0") value.as_mut_ptr(),
in("x1") carrier,
in("x2") conformance.witness_table(),
lateout("x0") boxed,
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("x20") _, lateout("x21") _,
clobber_abi("C"),
);
assert!(boxed != 0, "AnyTransition.init returned nothing");
super::opaque_modifier_for_transition(view, boxed)
}
}
pub trait Animating: Send + Sync + 'static {
fn progress(&self, time: f64) -> Option<f64>;
}
impl<F> Animating for F
where
F: Fn(f64) -> Option<f64> + Send + Sync + 'static,
{
fn progress(&self, time: f64) -> Option<f64> {
self(time)
}
}
pub struct CustomAnimationCurve {
index: i64,
}
fn animation_registry() -> &'static Mutex<Vec<&'static dyn Animating>> {
static R: OnceLock<Mutex<Vec<&'static dyn Animating>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn animation_fn<A: Animating>(curve: A) -> CustomAnimationCurve {
let leaked: &'static dyn Animating = Box::leak(Box::new(curve));
let mut r = animation_registry().lock().expect("animation registry");
r.push(leaked);
CustomAnimationCurve { index: r.len() as i64 - 1 }
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_custom_animation_thunk",
"_rswift_custom_animation_thunk:",
"mov x5, x20",
"mov x4, x3",
"mov x3, x2",
"mov x2, x1",
"mov x1, x0",
"mov x0, x8",
"b _rswift_custom_animation_animate",
);
unsafe extern "C" {
#[link_name = "rswift_custom_animation_thunk"]
fn custom_animation_thunk();
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments)]
unsafe extern "C" fn rswift_custom_animation_animate(
out: *mut u8,
value: *const c_void,
_context: *mut c_void,
v_meta: *const c_void,
v_wt: *const c_void,
this: *const u8,
time: f64,
) {
let index = unsafe { *(this as *const i64) } as usize;
let curve = {
let r = animation_registry().lock().expect("animation registry");
r.get(index).copied().expect("animation index out of range")
};
unsafe {
match curve.progress(time) {
None => abi::store_optional_tag_at(out, v_meta, false),
Some(fraction) => {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(v_meta);
(vwt.initialize_with_copy)(
out as *mut c_void,
value as *mut c_void,
v_meta,
);
core::arch::asm!("blr {f}", f = in(reg) resolve::vector_scale_by(),
in("d0") fraction,
in("x0") v_meta,
in("x1") v_wt,
in("x20") out,
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"),
);
abi::store_optional_tag_at(out, v_meta, true);
}
}
}
}
fn animation_carrier() -> (*const c_void, *const c_void) {
static C: OnceLock<(usize, usize)> = OnceLock::new();
let (d, m) = *C.get_or_init(|| unsafe {
let (d, m) =
crate::state::emit_int_carrier(b"RustAnimation\0", b"6rswift13RustAnimationV\0");
(d as usize, m as usize)
});
(d as *const c_void, m as *const c_void)
}
fn animation_conformance() -> &'static Conformance {
static C: OnceLock<Conformance> = OnceLock::new();
C.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI15CustomAnimationMp");
assert_eq!(
protocol.requirement_count(),
4,
"CustomAnimation changed shape — it used to be Hashable, `animate` and \
two defaulted requirements",
);
synthesize(
&protocol,
animation_carrier().0,
&[
Witness::Metadata(resolve::int_hashable_wt()),
Witness::Function(custom_animation_thunk as *const c_void),
Witness::Default,
Witness::Default,
],
)
})
}
impl CustomAnimationCurve {
pub fn animation_object(&self) -> u64 {
unsafe { self.build_animation() }
}
unsafe fn build_animation(&self) -> u64 {
unsafe { build_custom_animation(self.index) }
}
}
unsafe fn build_custom_animation(index: i64) -> u64 {
unsafe {
let conformance = animation_conformance();
let carrier = animation_carrier().1;
let mut held = vec![0u8; abi::type_bytes(carrier)];
held[..8].copy_from_slice(&index.to_le_bytes());
let anim: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::animation_custom_init(),
in("x0") held.as_mut_ptr(),
in("x1") carrier,
in("x2") conformance.witness_table(),
lateout("x0") anim,
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("x20") _, lateout("x21") _,
clobber_abi("C"),
);
assert!(anim != 0, "Animation.init returned nothing");
anim
}
}
pub fn custom_animation(
view: &ViewHandle,
curve: &CustomAnimationCurve,
value: i64,
) -> ViewHandle {
unsafe {
let anim = build_custom_animation(curve.index);
super::animation_word(view, anim, value)
}
}