1use super::{wrap_owned, ViewHandle};
39use crate::abi;
40use crate::conformance::{synthesize, Conformance, Protocol, Witness};
41use crate::resolve;
42use core::ffi::c_void;
43use std::sync::{Mutex, OnceLock};
44
45pub struct ButtonConfiguration<'a> {
47 raw: *mut c_void,
48 marker: core::marker::PhantomData<&'a ()>,
49}
50
51impl ButtonConfiguration<'_> {
52 pub fn label(&self) -> ViewHandle {
54 unsafe { config_view(resolve::button_config_label(), resolve::button_config_label_ma(), self.raw) }
55 }
56
57 pub fn is_pressed(&self) -> bool {
62 unsafe { config_flag(resolve::button_config_pressed(), self.raw) }
63 }
64}
65
66pub struct LabelConfiguration<'a> {
68 raw: *mut c_void,
69 marker: core::marker::PhantomData<&'a ()>,
70}
71
72impl LabelConfiguration<'_> {
73 pub fn title(&self) -> ViewHandle {
75 unsafe { config_view(resolve::label_config_title(), resolve::label_config_title_ma(), self.raw) }
76 }
77
78 pub fn icon(&self) -> ViewHandle {
80 unsafe { config_view(resolve::label_config_icon(), resolve::label_config_icon_ma(), self.raw) }
81 }
82}
83
84unsafe fn config_view(
95 getter: *const c_void,
96 ma: *const c_void,
97 raw: *mut c_void,
98) -> ViewHandle {
99 unsafe {
100 let meta = abi::metadata0(ma);
101 assert!(!meta.is_null(), "style configuration property metadata");
102 let wt = abi::get_view_wt(meta);
103 assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
104 let mut out = vec![0u8; abi::type_bytes(meta)];
105 core::arch::asm!("blr {f}", f = in(reg) getter,
106 in("x8") out.as_mut_ptr(),
107 in("x20") raw,
108 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
109 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
110 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
111 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
112 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
113 );
114 wrap_owned(out, meta, wt)
115 }
116}
117
118unsafe fn config_flag(getter: *const c_void, raw: *mut c_void) -> bool {
123 unsafe {
124 let out: u64;
125 core::arch::asm!("blr {f}", f = in(reg) getter,
126 in("x20") raw,
127 lateout("x0") out,
128 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
129 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
130 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
131 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
132 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
133 );
134 out & 1 != 0
135 }
136}
137
138pub trait ButtonStyling: Send + Sync + 'static {
142 fn make_body(&self, configuration: ButtonConfiguration<'_>) -> ViewHandle;
145}
146
147impl<F> ButtonStyling for F
148where
149 F: Fn(ButtonConfiguration<'_>) -> ViewHandle + Send + Sync + 'static,
150{
151 fn make_body(&self, configuration: ButtonConfiguration<'_>) -> ViewHandle {
152 self(configuration)
153 }
154}
155
156pub trait LabelStyling: Send + Sync + 'static {
158 fn make_body(&self, configuration: LabelConfiguration<'_>) -> ViewHandle;
160}
161
162impl<F> LabelStyling for F
163where
164 F: Fn(LabelConfiguration<'_>) -> ViewHandle + Send + Sync + 'static,
165{
166 fn make_body(&self, configuration: LabelConfiguration<'_>) -> ViewHandle {
167 self(configuration)
168 }
169}
170
171pub struct CustomButtonStyle {
177 index: i64,
178}
179
180pub struct CustomLabelStyle {
182 index: i64,
183}
184
185fn button_registry() -> &'static Mutex<Vec<&'static dyn ButtonStyling>> {
186 static R: OnceLock<Mutex<Vec<&'static dyn ButtonStyling>>> = OnceLock::new();
187 R.get_or_init(|| Mutex::new(Vec::new()))
188}
189
190fn label_registry() -> &'static Mutex<Vec<&'static dyn LabelStyling>> {
191 static R: OnceLock<Mutex<Vec<&'static dyn LabelStyling>>> = OnceLock::new();
192 R.get_or_init(|| Mutex::new(Vec::new()))
193}
194
195pub fn button_style_fn<S: ButtonStyling>(style: S) -> CustomButtonStyle {
197 let leaked: &'static dyn ButtonStyling = Box::leak(Box::new(style));
198 let mut r = button_registry().lock().expect("button style registry");
199 r.push(leaked);
200 CustomButtonStyle { index: r.len() as i64 - 1 }
201}
202
203pub fn label_style_fn<S: LabelStyling>(style: S) -> CustomLabelStyle {
205 let leaked: &'static dyn LabelStyling = Box::leak(Box::new(style));
206 let mut r = label_registry().lock().expect("label style registry");
207 r.push(leaked);
208 CustomLabelStyle { index: r.len() as i64 - 1 }
209}
210
211core::arch::global_asm!(
220 ".p2align 2",
221 ".globl _rswift_button_style_thunk",
222 "_rswift_button_style_thunk:",
223 "mov x2, x20",
224 "mov x1, x0",
225 "mov x0, x8",
226 "b _rswift_button_make_body",
227 ".p2align 2",
228 ".globl _rswift_label_style_thunk",
229 "_rswift_label_style_thunk:",
230 "mov x2, x20",
231 "mov x1, x0",
232 "mov x0, x8",
233 "b _rswift_label_make_body",
234);
235
236unsafe extern "C" {
237 #[link_name = "rswift_button_style_thunk"]
238 fn button_style_thunk();
239 #[link_name = "rswift_label_style_thunk"]
240 fn label_style_thunk();
241}
242
243#[unsafe(no_mangle)]
249unsafe extern "C" fn rswift_button_make_body(
250 out: *mut u8,
251 configuration: *mut c_void,
252 this: *const u8,
253) {
254 let index = unsafe { *(this as *const i64) } as usize;
255 let style = {
256 let r = button_registry().lock().expect("button style registry");
257 r.get(index).copied().expect("button style index out of range")
258 };
259 let body = style.make_body(ButtonConfiguration {
260 raw: configuration,
261 marker: core::marker::PhantomData,
262 });
263 unsafe { *(out as *mut u64) = body.into_anyview_word() };
265}
266
267#[unsafe(no_mangle)]
272unsafe extern "C" fn rswift_label_make_body(
273 out: *mut u8,
274 configuration: *mut c_void,
275 this: *const u8,
276) {
277 let index = unsafe { *(this as *const i64) } as usize;
278 let style = {
279 let r = label_registry().lock().expect("label style registry");
280 r.get(index).copied().expect("label style index out of range")
281 };
282 let body = style.make_body(LabelConfiguration {
283 raw: configuration,
284 marker: core::marker::PhantomData,
285 });
286 unsafe { *(out as *mut u64) = body.into_anyview_word() };
287}
288
289fn button_carrier() -> (*const c_void, *const c_void) {
291 static C: OnceLock<(usize, usize)> = OnceLock::new();
292 let (d, m) = *C.get_or_init(|| unsafe {
293 let (d, m) =
294 crate::state::emit_int_carrier(b"RustButtonStyle\0", b"6rswift15RustButtonStyleV\0");
295 (d as usize, m as usize)
296 });
297 (d as *const c_void, m as *const c_void)
298}
299
300fn label_carrier() -> (*const c_void, *const c_void) {
304 static C: OnceLock<(usize, usize)> = OnceLock::new();
305 let (d, m) = *C.get_or_init(|| unsafe {
306 let (d, m) =
307 crate::state::emit_int_carrier(b"RustLabelStyle\0", b"6rswift14RustLabelStyleV\0");
308 (d as usize, m as usize)
309 });
310 (d as *const c_void, m as *const c_void)
311}
312
313unsafe fn style_conformance(
321 protocol: &'static core::ffi::CStr,
322 descriptor: *const c_void,
323 make_body: *const c_void,
324) -> Conformance {
325 unsafe {
326 let protocol = Protocol::from_symbol(protocol);
327 assert_eq!(protocol.requirement_count(), 3, "the style protocol changed shape");
328 synthesize(
329 &protocol,
330 descriptor,
331 &[
332 Witness::Metadata(resolve::anyview_wt()),
333 Witness::Metadata(resolve::anyview_meta()),
334 Witness::Function(make_body),
335 ],
336 )
337 }
338}
339
340fn button_conformance() -> &'static Conformance {
341 static C: OnceLock<Conformance> = OnceLock::new();
342 C.get_or_init(|| unsafe {
343 style_conformance(
344 c"$s7SwiftUI11ButtonStyleMp",
345 button_carrier().0,
346 button_style_thunk as *const c_void,
347 )
348 })
349}
350
351fn label_conformance() -> &'static Conformance {
352 static C: OnceLock<Conformance> = OnceLock::new();
353 C.get_or_init(|| unsafe {
354 style_conformance(
355 c"$s7SwiftUI10LabelStyleMp",
356 label_carrier().0,
357 label_style_thunk as *const c_void,
358 )
359 })
360}
361
362unsafe fn apply_style(
372 view: &ViewHandle,
373 index: i64,
374 conformance: &Conformance,
375 carrier: (*const c_void, *const c_void),
376 f: *const c_void,
377 oret: *const c_void,
378) -> ViewHandle {
379 unsafe {
380 let meta = carrier.1;
381 let mut value = vec![0u8; abi::type_bytes(meta)];
382 value[..8].copy_from_slice(&index.to_le_bytes());
383
384 let subst = [view.meta, meta, view.wt, conformance.witness_table()];
385 let (out_meta, out_wt, size) = super::opaque_result_type(oret, &subst);
386 let mut result = vec![0u8; size];
387 let this = view.value_ptr();
388 core::arch::asm!("blr {f}", f = in(reg) f,
391 in("x0") value.as_mut_ptr(),
392 in("x1") view.meta,
393 in("x2") meta,
394 in("x3") view.wt,
395 in("x4") conformance.witness_table(),
396 in("x8") result.as_mut_ptr(),
397 in("x20") this,
398 lateout("x5") _, lateout("x6") _, lateout("x7") _,
399 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
400 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
401 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
402 );
403 wrap_owned(result, out_meta, out_wt)
404 }
405}
406
407pub fn custom_button_style(view: &ViewHandle, style: &CustomButtonStyle) -> ViewHandle {
409 unsafe {
410 apply_style(
411 view,
412 style.index,
413 button_conformance(),
414 button_carrier(),
415 resolve::button_style_custom_fn(),
416 resolve::button_style_custom_oret(),
417 )
418 }
419}
420
421pub fn custom_label_style(view: &ViewHandle, style: &CustomLabelStyle) -> ViewHandle {
423 unsafe {
424 apply_style(
425 view,
426 style.index,
427 label_conformance(),
428 label_carrier(),
429 resolve::label_style_fn(),
430 resolve::label_style_oret(),
431 )
432 }
433}
434
435pub trait Modifying: Send + Sync + 'static {
450 fn body(&self, content: ViewHandle) -> ViewHandle;
452}
453
454impl<F> Modifying for F
455where
456 F: Fn(ViewHandle) -> ViewHandle + Send + Sync + 'static,
457{
458 fn body(&self, content: ViewHandle) -> ViewHandle {
459 self(content)
460 }
461}
462
463pub struct CustomModifier {
465 index: i64,
466}
467
468fn modifier_registry() -> &'static Mutex<Vec<&'static dyn Modifying>> {
469 static R: OnceLock<Mutex<Vec<&'static dyn Modifying>>> = OnceLock::new();
470 R.get_or_init(|| Mutex::new(Vec::new()))
471}
472
473pub fn modifier_fn<M: Modifying>(modifier: M) -> CustomModifier {
475 let leaked: &'static dyn Modifying = Box::leak(Box::new(modifier));
476 let mut r = modifier_registry().lock().expect("modifier registry");
477 r.push(leaked);
478 CustomModifier { index: r.len() as i64 - 1 }
479}
480
481core::arch::global_asm!(
484 ".p2align 2",
485 ".globl _rswift_view_modifier_thunk",
486 "_rswift_view_modifier_thunk:",
487 "mov x2, x20",
488 "mov x1, x0",
489 "mov x0, x8",
490 "b _rswift_view_modifier_body",
491);
492
493unsafe extern "C" {
494 #[link_name = "rswift_view_modifier_thunk"]
495 fn view_modifier_thunk();
496}
497
498static CONTENT_META: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
505static CONTENT_WT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
506
507#[unsafe(no_mangle)]
513unsafe extern "C" fn rswift_view_modifier_body(
514 out: *mut u8,
515 content: *mut c_void,
516 this: *const u8,
517) {
518 use std::sync::atomic::Ordering::Acquire;
519 let index = unsafe { *(this as *const i64) } as usize;
520 let modifier = {
521 let r = modifier_registry().lock().expect("modifier registry");
522 r.get(index).copied().expect("modifier index out of range")
523 };
524 let meta = CONTENT_META.load(Acquire) as *const c_void;
525 let wt = CONTENT_WT.load(Acquire) as *const c_void;
526 let handle = unsafe {
529 let mut bytes = vec![0u8; abi::type_bytes(meta)];
530 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(meta);
531 (vwt.initialize_with_copy)(bytes.as_mut_ptr() as *mut c_void, content, meta);
532 wrap_owned(bytes, meta, wt)
533 };
534 let body = modifier.body(handle);
535 unsafe { *(out as *mut u64) = body.into_anyview_word() };
536}
537
538fn modifier_carrier() -> (*const c_void, *const c_void) {
540 static C: OnceLock<(usize, usize)> = OnceLock::new();
541 let (d, m) = *C.get_or_init(|| unsafe {
542 let (d, m) =
543 crate::state::emit_int_carrier(b"RustModifier\0", b"6rswift12RustModifierV\0");
544 (d as usize, m as usize)
545 });
546 (d as *const c_void, m as *const c_void)
547}
548
549fn modifier_conformance() -> &'static Conformance {
550 static C: OnceLock<Conformance> = OnceLock::new();
551 C.get_or_init(|| unsafe {
552 let protocol = Protocol::from_symbol(c"$s7SwiftUI12ViewModifierMp");
553 assert_eq!(
554 protocol.requirement_count(),
555 6,
556 "ViewModifier changed shape — it used to be Body, its conformance, \
557 three defaulted `_make…` entries and `body(content:)`",
558 );
559 let conformance = synthesize(
560 &protocol,
561 modifier_carrier().0,
562 &[
563 Witness::Metadata(resolve::anyview_wt()),
564 Witness::Metadata(resolve::anyview_meta()),
565 Witness::Default,
566 Witness::Default,
567 Witness::Default,
568 Witness::Function(view_modifier_thunk as *const c_void),
569 ],
570 );
571
572 use std::sync::atomic::Ordering::Release;
575 let meta = abi::generic_metadata2(
576 resolve::vm_content_ma(),
577 modifier_carrier().1,
578 conformance.witness_table(),
579 );
580 assert!(!meta.is_null(), "_ViewModifier_Content<RustModifier> metadata");
581 let wt = abi::get_view_wt(meta);
582 assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
583 CONTENT_META.store(meta as usize, Release);
584 CONTENT_WT.store(wt as usize, Release);
585
586 conformance
587 })
588}
589
590pub fn custom_modifier(view: &ViewHandle, modifier: &CustomModifier) -> ViewHandle {
597 unsafe {
598 let conformance = modifier_conformance();
599 let carrier = modifier_carrier().1;
600 let mut value = vec![0u8; abi::type_bytes(carrier)];
601 value[..8].copy_from_slice(&modifier.index.to_le_bytes());
602
603 let meta = abi::generic_metadata2(resolve::modified_content_ma(), view.meta, carrier);
604 assert!(!meta.is_null(), "ModifiedContent metadata");
605 let wt = abi::get_view_wt(meta);
606 assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
607
608 let mut result = vec![0u8; abi::type_bytes(meta)];
609 let this = view.value_ptr();
610 core::arch::asm!("blr {f}", f = in(reg) resolve::apply_view_modifier_fn(),
613 in("x0") value.as_mut_ptr(),
614 in("x1") view.meta,
615 in("x2") carrier,
616 in("x3") view.wt,
617 in("x8") result.as_mut_ptr(),
618 in("x20") this,
619 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
620 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
621 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
622 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
623 );
624 let _ = conformance;
625 wrap_owned(result, meta, wt)
626 }
627}
628
629#[derive(Clone, Copy, Debug, PartialEq, Eq)]
642pub enum TransitionPhase {
643 WillAppear,
645 Identity,
647 DidDisappear,
649}
650
651impl TransitionPhase {
652 fn case(self) -> &'static str {
654 match self {
655 Self::WillAppear => "willAppear",
656 Self::Identity => "identity",
657 Self::DidDisappear => "didDisappear",
658 }
659 }
660
661 fn from_tag(tag: u8) -> Self {
668 unsafe {
669 let meta = abi::metadata0(resolve::transition_phase_ma());
670 assert!(!meta.is_null(), "TransitionPhase metadata");
671 for p in [Self::WillAppear, Self::Identity, Self::DidDisappear] {
672 let want = abi::enum_case_index(meta, p.case())
673 .unwrap_or_else(|| panic!("TransitionPhase has no `{}` case", p.case()));
674 if want as u8 == tag {
675 return p;
676 }
677 }
678 panic!("TransitionPhase tag {tag} is none of the three cases");
679 }
680 }
681}
682
683pub trait Transitioning: Send + Sync + 'static {
685 fn body(&self, content: ViewHandle, phase: TransitionPhase) -> ViewHandle;
687}
688
689impl<F> Transitioning for F
690where
691 F: Fn(ViewHandle, TransitionPhase) -> ViewHandle + Send + Sync + 'static,
692{
693 fn body(&self, content: ViewHandle, phase: TransitionPhase) -> ViewHandle {
694 self(content, phase)
695 }
696}
697
698pub struct CustomTransition {
700 index: i64,
701}
702
703fn transition_registry() -> &'static Mutex<Vec<&'static dyn Transitioning>> {
704 static R: OnceLock<Mutex<Vec<&'static dyn Transitioning>>> = OnceLock::new();
705 R.get_or_init(|| Mutex::new(Vec::new()))
706}
707
708pub fn transition_fn<T: Transitioning>(transition: T) -> CustomTransition {
710 let leaked: &'static dyn Transitioning = Box::leak(Box::new(transition));
711 let mut r = transition_registry().lock().expect("transition registry");
712 r.push(leaked);
713 CustomTransition { index: r.len() as i64 - 1 }
714}
715
716core::arch::global_asm!(
719 ".p2align 2",
720 ".globl _rswift_transition_thunk",
721 "_rswift_transition_thunk:",
722 "mov x3, x20",
723 "mov x2, x1",
724 "mov x1, x0",
725 "mov x0, x8",
726 "b _rswift_transition_body",
727);
728
729unsafe extern "C" {
730 #[link_name = "rswift_transition_thunk"]
731 fn transition_thunk();
732}
733
734static PLACEHOLDER_META: std::sync::atomic::AtomicUsize =
735 std::sync::atomic::AtomicUsize::new(0);
736static PLACEHOLDER_WT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
737
738#[unsafe(no_mangle)]
744unsafe extern "C" fn rswift_transition_body(
745 out: *mut u8,
746 content: *mut c_void,
747 phase: u64,
748 this: *const u8,
749) {
750 use std::sync::atomic::Ordering::Acquire;
751 let index = unsafe { *(this as *const i64) } as usize;
752 let transition = {
753 let r = transition_registry().lock().expect("transition registry");
754 r.get(index).copied().expect("transition index out of range")
755 };
756 let meta = PLACEHOLDER_META.load(Acquire) as *const c_void;
757 let wt = PLACEHOLDER_WT.load(Acquire) as *const c_void;
758 let handle = unsafe {
759 let mut bytes = vec![0u8; abi::type_bytes(meta)];
760 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(meta);
761 (vwt.initialize_with_copy)(bytes.as_mut_ptr() as *mut c_void, content, meta);
762 wrap_owned(bytes, meta, wt)
763 };
764 let body = transition.body(handle, TransitionPhase::from_tag(phase as u8));
765 unsafe { *(out as *mut u64) = body.into_anyview_word() };
766}
767
768fn transition_carrier() -> (*const c_void, *const c_void) {
770 static C: OnceLock<(usize, usize)> = OnceLock::new();
771 let (d, m) = *C.get_or_init(|| unsafe {
772 let (d, m) =
773 crate::state::emit_int_carrier(b"RustTransition\0", b"6rswift14RustTransitionV\0");
774 (d as usize, m as usize)
775 });
776 (d as *const c_void, m as *const c_void)
777}
778
779fn transition_conformance() -> &'static Conformance {
780 static C: OnceLock<Conformance> = OnceLock::new();
781 C.get_or_init(|| unsafe {
782 let protocol = Protocol::from_symbol(c"$s7SwiftUI10TransitionMp");
783 assert_eq!(
784 protocol.requirement_count(),
785 5,
786 "Transition changed shape — it used to be Body, its conformance, \
787 `body(content:phase:)` and two defaulted requirements",
788 );
789 let conformance = synthesize(
790 &protocol,
791 transition_carrier().0,
792 &[
793 Witness::Metadata(resolve::anyview_wt()),
794 Witness::Metadata(resolve::anyview_meta()),
795 Witness::Function(transition_thunk as *const c_void),
796 Witness::Default,
797 Witness::Default,
798 ],
799 );
800
801 use std::sync::atomic::Ordering::Release;
802 let meta = abi::generic_metadata2(
803 resolve::placeholder_content_ma(),
804 transition_carrier().1,
805 conformance.witness_table(),
806 );
807 assert!(!meta.is_null(), "PlaceholderContentView<RustTransition> metadata");
808 let wt = abi::get_view_wt(meta);
809 assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
810 PLACEHOLDER_META.store(meta as usize, Release);
811 PLACEHOLDER_WT.store(wt as usize, Release);
812
813 conformance
814 })
815}
816
817pub fn custom_transition(view: &ViewHandle, transition: &CustomTransition) -> ViewHandle {
823 unsafe {
824 let conformance = transition_conformance();
825 let carrier = transition_carrier().1;
826 let mut value = vec![0u8; abi::type_bytes(carrier)];
827 value[..8].copy_from_slice(&transition.index.to_le_bytes());
828
829 let boxed: u64;
833 core::arch::asm!("blr {f}", f = in(reg) resolve::any_transition_init(),
834 in("x0") value.as_mut_ptr(),
835 in("x1") carrier,
836 in("x2") conformance.witness_table(),
837 lateout("x0") boxed,
838 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
839 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
840 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
841 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
842 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
843 clobber_abi("C"),
844 );
845 assert!(boxed != 0, "AnyTransition.init returned nothing");
846
847 super::opaque_modifier_for_transition(view, boxed)
848 }
849}
850
851pub trait Animating: Send + Sync + 'static {
866 fn progress(&self, time: f64) -> Option<f64>;
872}
873
874impl<F> Animating for F
875where
876 F: Fn(f64) -> Option<f64> + Send + Sync + 'static,
877{
878 fn progress(&self, time: f64) -> Option<f64> {
879 self(time)
880 }
881}
882
883pub struct CustomAnimationCurve {
885 index: i64,
886}
887
888fn animation_registry() -> &'static Mutex<Vec<&'static dyn Animating>> {
889 static R: OnceLock<Mutex<Vec<&'static dyn Animating>>> = OnceLock::new();
890 R.get_or_init(|| Mutex::new(Vec::new()))
891}
892
893pub fn animation_fn<A: Animating>(curve: A) -> CustomAnimationCurve {
895 let leaked: &'static dyn Animating = Box::leak(Box::new(curve));
896 let mut r = animation_registry().lock().expect("animation registry");
897 r.push(leaked);
898 CustomAnimationCurve { index: r.len() as i64 - 1 }
899}
900
901core::arch::global_asm!(
907 ".p2align 2",
908 ".globl _rswift_custom_animation_thunk",
909 "_rswift_custom_animation_thunk:",
910 "mov x5, x20",
911 "mov x4, x3",
912 "mov x3, x2",
913 "mov x2, x1",
914 "mov x1, x0",
915 "mov x0, x8",
916 "b _rswift_custom_animation_animate",
917);
918
919unsafe extern "C" {
920 #[link_name = "rswift_custom_animation_thunk"]
921 fn custom_animation_thunk();
922}
923
924#[unsafe(no_mangle)]
930#[allow(clippy::too_many_arguments)]
931unsafe extern "C" fn rswift_custom_animation_animate(
932 out: *mut u8,
933 value: *const c_void,
934 _context: *mut c_void,
935 v_meta: *const c_void,
936 v_wt: *const c_void,
937 this: *const u8,
938 time: f64,
939) {
940 let index = unsafe { *(this as *const i64) } as usize;
941 let curve = {
942 let r = animation_registry().lock().expect("animation registry");
943 r.get(index).copied().expect("animation index out of range")
944 };
945 unsafe {
946 match curve.progress(time) {
947 None => abi::store_optional_tag_at(out, v_meta, false),
951 Some(fraction) => {
952 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(v_meta);
953 (vwt.initialize_with_copy)(
954 out as *mut c_void,
955 value as *mut c_void,
956 v_meta,
957 );
958 core::arch::asm!("blr {f}", f = in(reg) resolve::vector_scale_by(),
963 in("d0") fraction,
964 in("x0") v_meta,
965 in("x1") v_wt,
966 in("x20") out,
967 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
968 lateout("x6") _, lateout("x7") _, lateout("x8") _,
969 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
970 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
971 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
972 );
973 abi::store_optional_tag_at(out, v_meta, true);
974 }
975 }
976 }
977}
978
979fn animation_carrier() -> (*const c_void, *const c_void) {
981 static C: OnceLock<(usize, usize)> = OnceLock::new();
982 let (d, m) = *C.get_or_init(|| unsafe {
983 let (d, m) =
984 crate::state::emit_int_carrier(b"RustAnimation\0", b"6rswift13RustAnimationV\0");
985 (d as usize, m as usize)
986 });
987 (d as *const c_void, m as *const c_void)
988}
989
990fn animation_conformance() -> &'static Conformance {
991 static C: OnceLock<Conformance> = OnceLock::new();
992 C.get_or_init(|| unsafe {
993 let protocol = Protocol::from_symbol(c"$s7SwiftUI15CustomAnimationMp");
994 assert_eq!(
995 protocol.requirement_count(),
996 4,
997 "CustomAnimation changed shape — it used to be Hashable, `animate` and \
998 two defaulted requirements",
999 );
1000 synthesize(
1001 &protocol,
1002 animation_carrier().0,
1003 &[
1004 Witness::Metadata(resolve::int_hashable_wt()),
1012 Witness::Function(custom_animation_thunk as *const c_void),
1013 Witness::Default,
1014 Witness::Default,
1015 ],
1016 )
1017 })
1018}
1019
1020impl CustomAnimationCurve {
1021 pub fn animation_object(&self) -> u64 {
1032 unsafe { self.build_animation() }
1033 }
1034
1035 unsafe fn build_animation(&self) -> u64 {
1038 unsafe { build_custom_animation(self.index) }
1039 }
1040}
1041
1042unsafe fn build_custom_animation(index: i64) -> u64 {
1047 unsafe {
1048 let conformance = animation_conformance();
1049 let carrier = animation_carrier().1;
1050 let mut held = vec![0u8; abi::type_bytes(carrier)];
1051 held[..8].copy_from_slice(&index.to_le_bytes());
1052
1053 let anim: u64;
1056 core::arch::asm!("blr {f}", f = in(reg) resolve::animation_custom_init(),
1057 in("x0") held.as_mut_ptr(),
1058 in("x1") carrier,
1059 in("x2") conformance.witness_table(),
1060 lateout("x0") anim,
1061 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
1062 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
1063 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1064 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1065 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1066 clobber_abi("C"),
1067 );
1068 assert!(anim != 0, "Animation.init returned nothing");
1069 anim
1070 }
1071}
1072
1073pub fn custom_animation(
1075 view: &ViewHandle,
1076 curve: &CustomAnimationCurve,
1077 value: i64,
1078) -> ViewHandle {
1079 unsafe {
1080 let anim = build_custom_animation(curve.index);
1081 super::animation_word(view, anim, value)
1082 }
1083}