1use super::*;
6use crate::abi;
7use crate::resolve;
8use core::ffi::c_void;
9use crate::mangle::{self, swift_type};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum InsettableShape {
21 Circle,
22 Ellipse,
23 Rectangle,
24 ContainerRelativeShape,
25}
26
27impl InsettableShape {
28 pub(crate) fn type_mangling(self) -> &'static str {
30 match self {
31 Self::Circle => "7SwiftUI6CircleV",
32 Self::Ellipse => "7SwiftUI7EllipseV",
33 Self::Rectangle => "7SwiftUI9RectangleV",
34 Self::ContainerRelativeShape => "7SwiftUI22ContainerRelativeShapeV",
35 }
36 }
37
38 pub(crate) fn parts(self) -> (*const c_void, *const c_void, *const c_void) {
41 match self {
42 Self::Circle => (
43 resolve::circle_ma(),
44 resolve::circle_insettable_wt(),
45 resolve::circle_init(),
46 ),
47 Self::Ellipse => (
48 resolve::ellipse_ma(),
49 resolve::ellipse_insettable_wt(),
50 resolve::ellipse_init(),
51 ),
52 Self::Rectangle => (
53 resolve::rectangle_ma(),
54 resolve::rectangle_insettable_wt(),
55 resolve::rectangle_init(),
56 ),
57 Self::ContainerRelativeShape => (
58 resolve::container_relative_shape_ma(),
59 resolve::container_relative_shape_insettable_wt(),
60 resolve::container_relative_shape_init(),
61 ),
62 }
63 }
64}
65
66pub fn find_navigator(view: &ViewHandle, state: &BoundState<bool>) -> ViewHandle {
71 unsafe {
72 let mut b = bool_binding(state);
73 let w = binding_words(&mut b);
74 opaque_modifier(
75 resolve::find_navigator_fn(),
76 resolve::find_navigator_oret(),
77 view,
78 &w,
79 [0.0; 4],
80 )
81 }
82}
83
84pub fn dialog_suppression_toggle(view: &ViewHandle, state: &BoundState<bool>) -> ViewHandle {
86 unsafe {
87 let mut b = bool_binding(state);
88 let w = binding_words(&mut b);
89 opaque_modifier(
90 resolve::dialog_suppression_toggle_fn(),
91 resolve::dialog_suppression_toggle_oret(),
92 view,
93 &w,
94 [0.0; 4],
95 )
96 }
97}
98
99pub fn dialog_suppression_toggle_labelled(
105 view: &ViewHandle,
106 label: &str,
107 state: &BoundState<bool>,
108) -> ViewHandle {
109 unsafe {
110 let ss = abi::swift_string(label);
111 let k = localized_string_key(
112 u64::from_le_bytes(ss[..8].try_into().expect("lo")),
113 u64::from_le_bytes(ss[8..].try_into().expect("hi")),
114 );
115 let mut b = bool_binding(state);
116 let w = binding_words(&mut b);
117 opaque_modifier(
118 resolve::dialog_suppression_toggle_labelled_fn(),
119 resolve::dialog_suppression_toggle_labelled_oret(),
120 view,
121 &[k.0, k.1, k.2, k.3, w[0], w[1], w[2]],
122 [0.0; 4],
123 )
124 }
125}
126
127pub fn high_priority_gesture(view: &ViewHandle, minimum_distance: f64, action: impl Fn() + 'static) -> ViewHandle {
133 unsafe {
134 gesture_variant(
135 resolve::high_priority_gesture_fn(),
136 resolve::high_priority_gesture_oret(),
137 view,
138 minimum_distance,
139 action,
140 )
141 }
142}
143
144pub fn simultaneous_gesture(view: &ViewHandle, minimum_distance: f64, action: impl Fn() + 'static) -> ViewHandle {
150 unsafe {
151 gesture_variant(
152 resolve::simultaneous_gesture_fn(),
153 resolve::simultaneous_gesture_oret(),
154 view,
155 minimum_distance,
156 action,
157 )
158 }
159}
160
161pub fn container_shape(view: &ViewHandle, shape: InsettableShape) -> ViewHandle {
169 unsafe {
170 let (meta, wt, init) = shape.parts();
171 style_modifier(
172 resolve::container_shape_fn(),
173 resolve::container_shape_oret(),
174 view,
175 meta,
176 wt,
177 init,
178 )
179 }
180}
181
182pub(crate) unsafe fn generic1_modifier(
197 func: *const c_void,
198 oret: *const c_void,
199 view: &ViewHandle,
200 arg_meta: *const c_void,
201 arg_wt: *const c_void,
202 arg: &[u8],
203 extra: &[u64],
204) -> ViewHandle {
205 unsafe {
206 let subst = [view.meta, arg_meta, view.wt, arg_wt];
207 let (meta, wt, size) = opaque_result_type(oret, &subst);
208 let mut ints = Vec::with_capacity(5 + extra.len());
209 ints.push(arg.as_ptr() as u64);
210 ints.extend_from_slice(extra);
211 ints.push(view.meta as u64);
212 ints.push(arg_meta as u64);
213 ints.push(view.wt as u64);
214 ints.push(arg_wt as u64);
215 let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, [0.0; 4], size);
216 wrap_owned(r, meta, wt)
217 }
218}
219
220#[derive(Clone, Copy, Debug, PartialEq)]
226pub enum HashableValue<'a> {
227 Str(&'a str),
228 Int(i64),
229 Bool(bool),
230 Double(f64),
231}
232
233impl HashableValue<'_> {
234 pub(crate) unsafe fn parts(self) -> (Vec<u8>, *const c_void, *const c_void) {
236 unsafe {
237 match self {
238 Self::Str(s) => (
239 abi::swift_string(s).to_vec(),
240 resolve::string_meta(),
241 resolve::string_hashable_wt(),
242 ),
243 Self::Int(i) => {
244 (i.to_le_bytes().to_vec(), resolve::int_meta(), resolve::int_hashable_wt())
245 }
246 Self::Bool(b) => {
247 (vec![u8::from(b)], resolve::bool_meta(), resolve::bool_hashable_wt())
248 }
249 Self::Double(d) => {
250 (d.to_le_bytes().to_vec(), resolve::double_meta(), resolve::double_hashable_wt())
251 }
252 }
253 }
254 }
255}
256
257pub trait IntoHashable<'a> {
268 fn into_hashable(self) -> HashableValue<'a>;
270}
271
272impl<'a> IntoHashable<'a> for HashableValue<'a> {
273 fn into_hashable(self) -> HashableValue<'a> {
274 self
275 }
276}
277
278impl<'a> IntoHashable<'a> for &'a str {
279 fn into_hashable(self) -> HashableValue<'a> {
280 HashableValue::Str(self)
281 }
282}
283
284impl<'a> IntoHashable<'a> for &'a String {
285 fn into_hashable(self) -> HashableValue<'a> {
286 HashableValue::Str(self)
287 }
288}
289
290impl IntoHashable<'_> for i64 {
291 fn into_hashable(self) -> HashableValue<'static> {
292 HashableValue::Int(self)
293 }
294}
295
296impl IntoHashable<'_> for i32 {
297 fn into_hashable(self) -> HashableValue<'static> {
298 HashableValue::Int(i64::from(self))
302 }
303}
304
305impl IntoHashable<'_> for usize {
306 fn into_hashable(self) -> HashableValue<'static> {
307 HashableValue::Int(self as i64)
308 }
309}
310
311impl IntoHashable<'_> for bool {
312 fn into_hashable(self) -> HashableValue<'static> {
313 HashableValue::Bool(self)
314 }
315}
316
317impl IntoHashable<'_> for f64 {
318 fn into_hashable(self) -> HashableValue<'static> {
319 HashableValue::Double(self)
320 }
321}
322
323pub fn id(view: &ViewHandle, value: HashableValue<'_>) -> ViewHandle {
325 unsafe {
326 let (bytes, meta, wt) = value.parts();
327 generic1_modifier(resolve::id_fn(), resolve::id_oret(), view, meta, wt, &bytes, &[])
328 }
329}
330
331pub unsafe fn apply_modifier(
344 view: &ViewHandle,
345 modifier_meta: *const c_void,
346 bytes: &[u8],
347) -> ViewHandle {
348 unsafe {
349 let content = abi::generic_metadata2(
354 resolve::modified_content_ma(),
355 view.meta,
356 modifier_meta,
357 );
358 let wt = abi::get_view_wt(content);
359 assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(content));
360 let ints = [
361 bytes.as_ptr() as u64,
362 view.meta as u64,
363 modifier_meta as u64,
364 view.wt as u64,
365 ];
366 let r = abi::call_modifier_generic(resolve::view_modifier_fn(), view.value_ptr(),
367 &ints,
368 [0.0; 4],
369 abi::type_bytes(content),
370 );
371 wrap_owned(r, content, wt)
372 }
373}
374
375pub fn tag(view: &ViewHandle, value: HashableValue<'_>) -> ViewHandle {
384 unsafe {
385 let (bytes, meta, wt) = value.parts();
386 let modifier = abi::generic_metadata2(resolve::tag_trait_writing_modifier_ma(), meta, wt);
387 let size = abi::type_size(modifier);
388 let at = abi::type_size(meta);
389 assert_eq!(size, at + 1, "_TagTraitWritingModifier is not tag + Bool");
390 let mut v = vec![0u8; abi::type_bytes(modifier)];
391 v[..bytes.len()].copy_from_slice(&bytes);
392 v[at] = 1;
394 apply_modifier(view, modifier, &v)
395 }
396}
397
398impl InsettableShape {
399 pub(crate) fn shape_parts(self) -> (*const c_void, *const c_void, *const c_void) {
404 match self {
405 Self::Circle => (resolve::circle_ma(), resolve::circle_shape_wt(), resolve::circle_init()),
406 Self::Ellipse => (resolve::ellipse_ma(), resolve::ellipse_shape_wt(), resolve::ellipse_init()),
407 Self::Rectangle => (resolve::rectangle_ma(), resolve::rectangle_shape_wt(), resolve::rectangle_init()),
408 Self::ContainerRelativeShape => (
409 resolve::container_relative_shape_ma(),
410 resolve::container_relative_shape_shape_wt(),
411 resolve::container_relative_shape_init(),
412 ),
413 }
414 }
415}
416
417pub fn content_shape(view: &ViewHandle, shape: InsettableShape, eo_fill: bool) -> ViewHandle {
419 unsafe {
420 let (ma, wt, init) = shape.shape_parts();
421 let meta = abi::metadata0(ma);
422 let mut value = vec![0u8; abi::type_bytes(meta)];
423 abi::call_out(init, [0; 8], [0.0; 4], meta as u64, value.as_mut_ptr());
424 generic1_modifier(
425 resolve::content_shape_fn(),
426 resolve::content_shape_oret(),
427 view,
428 meta,
429 wt,
430 &value,
431 &[u64::from(eo_fill)],
432 )
433 }
434}
435
436pub(crate) unsafe fn focus_binding(state_type: &[u8], init: *const c_void, projected: *const c_void) -> [u64; 3] {
448 unsafe {
449 let state_meta = abi::resolve_type_by_mangled_name(state_type);
450 assert!(!state_meta.is_null(), "focus state metadata");
451 let mut this = [0u64; 3];
458 core::arch::asm!("blr {f}", f = in(reg) init,
459 in("x8") this.as_mut_ptr(), in("x20") state_meta,
460 lateout("x0") _, lateout("x1") _, lateout("x2") _,
461 lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
462 lateout("x7") _, lateout("x9") _, lateout("x10") _,
463 lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
464 lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
465 lateout("x21") _, clobber_abi("C"),
466 );
467 let mut out = vec![0u64; 3];
478 core::arch::asm!("blr {f}", f = in(reg) projected,
479 in("x0") state_meta, in("x1") resolve::bool_hashable_wt(),
480 in("x8") out.as_mut_ptr(), in("x20") this.as_mut_ptr(),
481 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
482 lateout("x6") _, lateout("x7") _, lateout("x9") _,
483 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
484 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
485 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
486 );
487 [out[0], out[1], out[2]]
488 }
489}
490
491pub fn focused(view: &ViewHandle) -> ViewHandle {
493 unsafe {
494 let b = focus_binding(
495 mangle::bound(&swift_type!(SwiftUI.FocusState), &[mangle::BOOL]).as_bytes(),
496 resolve::focus_state_bool_init(),
497 resolve::focus_state_projected(),
498 );
499 opaque_modifier(resolve::focused_fn(), resolve::focused_oret(), view, &b, [0.0; 4])
500 }
501}
502
503pub fn focused_on(view: &ViewHandle, projection: [u64; 3]) -> ViewHandle {
511 unsafe {
512 opaque_modifier(
513 resolve::focused_fn(),
514 resolve::focused_oret(),
515 view,
516 &projection,
517 [0.0; 4],
518 )
519 }
520}
521
522pub fn search_focused(view: &ViewHandle) -> ViewHandle {
524 unsafe {
525 let b = focus_binding(
526 mangle::bound(&swift_type!(SwiftUI.FocusState), &[mangle::BOOL]).as_bytes(),
527 resolve::focus_state_bool_init(),
528 resolve::focus_state_projected(),
529 );
530 opaque_modifier(
531 resolve::search_focused_fn(),
532 resolve::search_focused_oret(),
533 view,
534 &b,
535 [0.0; 4],
536 )
537 }
538}
539
540pub fn accessibility_focused(view: &ViewHandle) -> ViewHandle {
542 unsafe {
543 let b = focus_binding(
544 mangle::bound(&swift_type!(SwiftUI.AccessibilityFocusState), &[mangle::BOOL]).as_bytes(),
545 resolve::a11y_focus_state_bool_init(),
546 resolve::a11y_focus_state_projected(),
547 );
548 opaque_modifier(
549 resolve::a11y_focused_fn(),
550 resolve::a11y_focused_oret(),
551 view,
552 &b,
553 [0.0; 4],
554 )
555 }
556}
557
558pub fn coordinate_space(view: &ViewHandle, name: HashableValue<'_>) -> ViewHandle {
560 unsafe {
561 let (bytes, meta, wt) = name.parts();
562 generic1_modifier(
563 resolve::coordinate_space_name_fn(),
564 resolve::coordinate_space_name_oret(),
565 view,
566 meta,
567 wt,
568 &bytes,
569 &[],
570 )
571 }
572}
573
574#[derive(Clone, Copy, Debug, PartialEq, Eq)]
579pub enum HashableKind {
580 Str,
581 Int,
582}
583
584pub fn scroll_position_id(
589 view: &ViewHandle,
590 state: &BoundState<String>,
591 anchor: Option<(f64, f64)>,
592) -> ViewHandle {
593 unsafe {
594 let g = state.clone();
595 let st = state.clone();
596 let value_meta = abi::resolve_type_by_mangled_name(mangle::optional(mangle::STRING).as_bytes());
597 let binding = binding_get_set(
598 value_meta,
599 mangle::bound(&swift_type!(SwiftUI.Binding), &[&mangle::optional(mangle::STRING)]).as_bytes(),
600 Box::new(move |out| {
601 let s = abi::swift_string(&g.get());
602 core::ptr::copy_nonoverlapping(s.as_ptr(), out, 16);
603 }),
604 Box::new(move |v| {
605 let mut raw = [0u8; 16];
606 core::ptr::copy_nonoverlapping(v, raw.as_mut_ptr(), 16);
607 st.set(abi::read_swift_string(raw));
608 }),
609 );
610 let mut ints = vec![binding.as_ptr() as u64];
613 ints.extend(opt_unit_point_words(anchor));
614 generic1_modifier_ints(
615 resolve::scroll_position_id_fn(),
616 resolve::scroll_position_id_oret(),
617 view,
618 resolve::string_meta(),
619 resolve::string_hashable_wt(),
620 &ints,
621 [0.0; 4],
622 )
623 }
624}
625
626pub fn preference_int(view: &ViewHandle, value: i64) -> ViewHandle {
633 unsafe {
634 let wt = crate::conformance::preference_key().witness_table();
635 let meta = resolve::spacer_meta();
636 let mut boxed = value;
640 generic1_modifier_ints(
641 resolve::preference_fn(),
642 resolve::preference_oret(),
643 view,
644 meta,
645 wt,
646 &[meta as u64, &mut boxed as *mut i64 as u64],
647 [0.0; 4],
648 )
649 }
650}
651
652unsafe fn generic2_modifier_ints(
654 func: *const c_void,
655 oret: *const c_void,
656 view: &ViewHandle,
657 arg_meta: *const c_void,
658 wt_a: *const c_void,
659 wt_b: *const c_void,
660 leading: &[u64],
661) -> ViewHandle {
662 unsafe {
663 let subst = [view.meta, arg_meta, view.wt, wt_a, wt_b];
664 let (meta, wt, size) = opaque_result_type(oret, &subst);
665 let mut ints = leading.to_vec();
666 ints.push(view.meta as u64);
667 ints.push(arg_meta as u64);
668 ints.push(view.wt as u64);
669 ints.push(wt_a as u64);
670 ints.push(wt_b as u64);
671 let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, [0.0; 4], size);
672 wrap_owned(r, meta, wt)
673 }
674}
675
676pub(crate) static OBSERVED_PREFERENCE: std::sync::atomic::AtomicI64 =
678 std::sync::atomic::AtomicI64::new(i64::MIN);
679
680pub(crate) unsafe extern "C" fn record_preference(
681 a0: u64,
682 _a1: u64,
683 _a2: u64,
684 _payload: *mut c_void,
685) {
686 let v = unsafe { *(a0 as *const i64) };
688 OBSERVED_PREFERENCE.store(v, std::sync::atomic::Ordering::SeqCst);
689}
690
691pub fn on_preference_change_int(view: &ViewHandle) -> ViewHandle {
697 unsafe {
698 let wt = crate::conformance::preference_key().witness_table();
699 let meta = resolve::spacer_meta();
700 let c = wide_words(record_preference, core::ptr::null_mut());
701 generic2_modifier_ints(
702 resolve::on_preference_change_fn(),
703 resolve::on_preference_change_oret(),
704 view,
705 meta,
706 wt,
707 resolve::int_equatable_wt(),
708 &[meta as u64, c[0], c[1]],
709 )
710 }
711}
712
713pub fn observed_preference() -> Option<i64> {
715 let v = OBSERVED_PREFERENCE.load(std::sync::atomic::Ordering::SeqCst);
716 (v != i64::MIN).then_some(v)
717}
718
719pub(crate) unsafe extern "C" fn double_preference(
720 a0: u64,
721 _a1: u64,
722 _a2: u64,
723 _payload: *mut c_void,
724) {
725 unsafe {
727 let p = a0 as *mut i64;
728 *p *= 2;
729 }
730}
731
732pub fn transform_preference_double(view: &ViewHandle) -> ViewHandle {
734 unsafe {
735 let wt = crate::conformance::preference_key().witness_table();
736 let meta = resolve::spacer_meta();
737 let c = wide_words(double_preference, core::ptr::null_mut());
738 generic1_modifier_ints(
739 resolve::transform_preference_fn(),
740 resolve::transform_preference_oret(),
741 view,
742 meta,
743 wt,
744 &[meta as u64, c[0], c[1]],
745 [0.0; 4],
746 )
747 }
748}
749
750
751unsafe fn string_transferable() -> (*const c_void, *const c_void) {
759 unsafe {
760 let meta = resolve::string_meta();
761 let wt = abi::get_view_wt_for_proto(meta, resolve::transferable_proto());
762 assert!(!wt.is_null(), "String: Transferable");
763 (meta, wt)
764 }
765}
766
767pub(crate) unsafe fn swift_string_array(items: &[&str]) -> u64 {
769 unsafe {
770 let meta = resolve::string_meta();
771 let stride = abi::type_bytes(meta);
772 let mut bytes = vec![0u8; stride * items.len()];
773 for (i, s) in items.iter().enumerate() {
774 bytes[i * stride..i * stride + 16].copy_from_slice(&abi::swift_string(s));
775 }
776 abi::swift_array(meta, items.len(), &bytes)
777 }
778}
779
780pub(crate) unsafe fn read_string_array(array: u64) -> Vec<String> {
782 unsafe {
783 if array == 0 {
784 return Vec::new();
785 }
786 let stride = abi::type_bytes(resolve::string_meta());
787 let (base, count) = abi::array_parts(array);
788 (0..count)
789 .map(|i| {
790 let mut raw = [0u8; 16];
791 core::ptr::copy_nonoverlapping(base.add(i * stride), raw.as_mut_ptr(), 16);
792 abi::read_swift_string(raw)
793 })
794 .collect()
795 }
796}
797
798pub(crate) type StringsDecide = Box<dyn Fn(&[String]) -> bool>;
800
801pub(crate) type StringsObserve = Box<dyn Fn(&[String])>;
803
804pub(crate) struct StringsPayload {
806 pub(crate) array: u64,
807 pub(crate) decide: Option<StringsDecide>,
808 pub(crate) observe: Option<StringsObserve>,
809}
810
811pub(crate) unsafe extern "C" fn emit_strings(payload: *mut c_void, _arg: u64) -> u64 {
813 unsafe {
814 let p = &*(payload as *const StringsPayload);
815 swift_runtime_sys::RuntimeRaw::swift_retain(p.array as *mut c_void);
817 p.array
818 }
819}
820
821pub(crate) unsafe extern "C" fn decide_on_strings(payload: *mut c_void, arg: u64) -> u64 {
823 unsafe {
824 let p = &*(payload as *const StringsPayload);
825 let items = read_string_array(arg);
826 u64::from(p.decide.as_ref().map(|f| f(&items)).unwrap_or(false))
827 }
828}
829
830pub(crate) unsafe extern "C" fn observe_strings(payload: *mut c_void, arg: u64) -> u64 {
832 unsafe {
833 let p = &*(payload as *const StringsPayload);
834 if let Some(f) = p.observe.as_ref() {
835 f(&read_string_array(arg));
836 }
837 swift_runtime_sys::RuntimeRaw::swift_retain(arg as *mut c_void);
838 arg
839 }
840}
841
842fn strings_payload(array: u64) -> *mut c_void {
843 Box::leak(Box::new(StringsPayload { array, decide: None, observe: None }))
844 as *mut StringsPayload as *mut c_void
845}
846
847fn callback_payload(
848 decide: Option<StringsDecide>,
849 observe: Option<StringsObserve>,
850) -> *mut c_void {
851 Box::leak(Box::new(StringsPayload { array: 0, decide, observe }))
852 as *mut StringsPayload as *mut c_void
853}
854
855pub fn copyable(view: &ViewHandle, items: &[&str]) -> ViewHandle {
857 unsafe {
858 let (meta, wt) = string_transferable();
859 let c = word_closure(emit_strings, strings_payload(swift_string_array(items)));
860 generic1_modifier_ints(
861 resolve::copyable_fn(),
862 resolve::copyable_oret(),
863 view,
864 meta,
865 wt,
866 &[c[0], c[1]],
867 [0.0; 4],
868 )
869 }
870}
871
872pub fn cuttable(view: &ViewHandle, items: &[&str]) -> ViewHandle {
874 unsafe {
875 let (meta, wt) = string_transferable();
876 let c = word_closure(emit_strings, strings_payload(swift_string_array(items)));
877 generic1_modifier_ints(
878 resolve::cuttable_fn(),
879 resolve::cuttable_oret(),
880 view,
881 meta,
882 wt,
883 &[meta as u64, c[0], c[1]],
884 [0.0; 4],
885 )
886 }
887}
888
889pub fn exportable_to_services(view: &ViewHandle, items: &[&str]) -> ViewHandle {
891 unsafe {
892 let (meta, wt) = string_transferable();
893 let c = word_closure(emit_strings, strings_payload(swift_string_array(items)));
894 generic1_modifier_ints(
895 resolve::exportable_to_services_fn(),
896 resolve::exportable_to_services_oret(),
897 view,
898 meta,
899 wt,
900 &[c[0], c[1]],
901 [0.0; 4],
902 )
903 }
904}
905
906pub fn importable_from_services(view: &ViewHandle, accept: impl Fn(&[String]) -> bool + 'static) -> ViewHandle {
910 unsafe {
911 let (meta, wt) = string_transferable();
912 let c = word_closure(decide_on_strings, callback_payload(Some(Box::new(accept)), None));
913 generic1_modifier_ints(
914 resolve::importable_from_services_fn(),
915 resolve::importable_from_services_oret(),
916 view,
917 meta,
918 wt,
919 &[meta as u64, c[0], c[1]],
920 [0.0; 4],
921 )
922 }
923}
924
925pub fn drop_destination(
930 view: &ViewHandle,
931 accept: impl Fn(&[String]) -> bool + 'static,
932 targeted: impl Fn(bool) + 'static,
933) -> ViewHandle {
934 unsafe {
935 let (meta, wt) = string_transferable();
936 let action = word_closure(decide_on_strings, callback_payload(Some(Box::new(accept)), None));
939 let is_targeted = bool_words(targeted);
940 generic1_modifier_ints(
941 resolve::drop_destination_fn(),
942 resolve::drop_destination_oret(),
943 view,
944 meta,
945 wt,
946 &[meta as u64, action[0], action[1], is_targeted[0], is_targeted[1]],
947 [0.0; 4],
948 )
949 }
950}
951
952pub fn paste_destination(view: &ViewHandle, paste: impl Fn(&[String]) + 'static) -> ViewHandle {
956 unsafe {
957 let (meta, wt) = string_transferable();
958 let action = wide_words(invoke_string_array, callback_payload(None, Some(Box::new(paste))));
959 let validator = word_closure(observe_strings, callback_payload(None, None));
960 generic1_modifier_ints(
961 resolve::paste_destination_fn(),
962 resolve::paste_destination_oret(),
963 view,
964 meta,
965 wt,
966 &[meta as u64, action[0], action[1], validator[0], validator[1]],
967 [0.0; 4],
968 )
969 }
970}
971
972pub(crate) unsafe extern "C" fn invoke_string_array(
974 a0: u64,
975 _a1: u64,
976 _a2: u64,
977 payload: *mut c_void,
978) {
979 unsafe {
980 let p = &*(payload as *const StringsPayload);
981 if let Some(f) = p.observe.as_ref() {
982 f(&read_string_array(a0));
983 }
984 }
985}
986
987pub fn scroll_position_edge(view: &ViewHandle, edge: super::Edge) -> ViewHandle {
997 unsafe {
998 let meta = abi::metadata0(resolve::scroll_position_ma());
999 assert!(!meta.is_null(), "ScrollPosition metadata");
1000 let mut initial = vec![0u8; abi::type_bytes(meta)];
1001 core::arch::asm!("blr {f}", f = in(reg) resolve::scroll_position_edge_init(),
1005 in("x0") resolve::never_meta(),
1006 in("x1") edge as u64,
1007 in("x2") resolve::never_meta(),
1008 in("x3") resolve::never_hashable_wt(),
1009 in("x8") initial.as_mut_ptr(),
1010 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1011 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1012 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1013 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1014 clobber_abi("C"),
1015 );
1016
1017 let cell: &'static std::sync::Mutex<Vec<u8>> =
1018 Box::leak(Box::new(std::sync::Mutex::new(initial)));
1019 let get_cell = cell;
1020 let binding = super::binding_get_set(
1021 meta,
1022 b"7SwiftUI7BindingVyAA14ScrollPositionVG",
1023 Box::new(move |out| {
1024 let v = get_cell.lock().expect("scroll position");
1025 core::ptr::copy_nonoverlapping(v.as_ptr(), out, v.len());
1026 }),
1027 Box::new(move |inp| {
1028 let mut v = cell.lock().expect("scroll position");
1029 let n = v.len();
1030 core::ptr::copy_nonoverlapping(inp, v.as_mut_ptr(), n);
1031 }),
1032 );
1033
1034 let subst = [view.meta, view.wt];
1035 let (out_meta, out_wt, size) =
1036 opaque_result_type(resolve::scroll_position_oret(), &subst);
1037 let mut result = vec![0u8; size];
1038 let this = view.value_ptr();
1039 core::arch::asm!("blr {f}", f = in(reg) resolve::scroll_position_fn(),
1043 in("x0") binding.as_ptr(),
1044 in("x1") 0u64,
1045 in("x2") 0u64,
1046 in("x3") 1u64,
1047 in("x4") view.meta,
1048 in("x5") view.wt,
1049 in("x8") result.as_mut_ptr(),
1050 in("x20") this,
1051 lateout("x6") _, lateout("x7") _,
1052 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1053 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1054 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1055 );
1056 wrap_owned(result, out_meta, out_wt)
1057 }
1058}
1059
1060struct DragText {
1063 text: String,
1064}
1065
1066unsafe extern "C" fn emit_drag_text(payload: *mut c_void) -> abi::Pair {
1075 unsafe {
1076 let d = &*(payload as *const DragText);
1077 let s = abi::swift_string(&d.text);
1078 abi::Pair(
1079 u64::from_le_bytes(s[..8].try_into().expect("lo")),
1080 u64::from_le_bytes(s[8..].try_into().expect("hi")),
1081 )
1082 }
1083}
1084
1085pub fn draggable_text(view: &ViewHandle, text: &str) -> ViewHandle {
1096 unsafe {
1097 let ctx = Box::leak(Box::new(abi::PairReturnClosureCtx::new(
1099 emit_drag_text,
1100 Box::into_raw(Box::new(DragText { text: text.to_string() })) as *mut c_void,
1101 ))) as *mut abi::PairReturnClosureCtx;
1102 generic1_modifier_ints(
1103 resolve::draggable_fn(),
1104 resolve::draggable_oret(),
1105 view,
1106 resolve::string_meta(),
1107 resolve::string_transferable_wt(),
1108 &[abi::pair_return_closure_thunk as *const () as u64, ctx as u64],
1109 [0.0; 4],
1110 )
1111 }
1112}
1113
1114pub fn draggable_container_item(view: &ViewHandle, id: HashableValue<'_>) -> ViewHandle {
1119 unsafe {
1120 let (bytes, meta, wt) = id.parts();
1121 let ns = crate::type_by_name!(swift_type!(SwiftUI.Namespace.ID).as_bytes());
1123 let opt = crate::type_by_name!(swift_type!(opt SwiftUI.Namespace.ID).as_bytes());
1124 let mut buf = vec![0u8; abi::type_bytes(opt)];
1125 abi::store_optional_tag(&mut buf, ns, false);
1126 let words: Vec<u64> = (0..abi::type_bytes(opt) / 8)
1127 .map(|i| u64::from_le_bytes(buf[i * 8..i * 8 + 8].try_into().expect("word")))
1128 .collect();
1129 let mut ints = vec![bytes.as_ptr() as u64];
1130 ints.extend(words);
1131 generic1_modifier_ints(
1132 resolve::draggable_container_fn(),
1133 resolve::draggable_container_oret(),
1134 view,
1135 meta,
1136 wt,
1137 &ints,
1138 [0.0; 4],
1139 )
1140 }
1141}
1142
1143pub(crate) unsafe extern "C" fn emit_measured_int(out: *mut u8, proxy: i64, payload: *mut c_void) {
1151 unsafe {
1152 let f = boxed_ref::<dyn Fn(f64, f64) -> i64>(payload);
1153 let (w, h) = crate::views::geometry_proxy_size(proxy as *const u8);
1154 *(out as *mut i64) = f(w, h);
1155 }
1156}
1157
1158pub(crate) unsafe extern "C" fn emit_measured_f64(out: *mut u8, proxy: i64, payload: *mut c_void) {
1163 unsafe {
1164 let f = boxed_ref::<dyn Fn(f64, f64) -> f64>(payload);
1165 let (w, h) = crate::views::geometry_proxy_size(proxy as *const u8);
1166 *(out as *mut f64) = f(w, h);
1167 }
1168}
1169
1170pub(crate) unsafe extern "C" fn invoke_f64_pair(
1172 a: *const u8,
1173 b: *const u8,
1174 payload: *mut c_void,
1175) {
1176 unsafe {
1177 let f = boxed_ref::<dyn Fn(f64, f64)>(payload);
1178 f(*(a as *const f64), *(b as *const f64));
1179 }
1180}
1181
1182pub(crate) unsafe extern "C" fn invoke_int_pair(
1187 a: *const u8,
1188 b: *const u8,
1189 payload: *mut c_void,
1190) {
1191 unsafe {
1192 let f = boxed_ref::<dyn Fn(i64, i64)>(payload);
1193 f(*(a as *const i64), *(b as *const i64));
1194 }
1195}
1196
1197pub fn on_geometry_change_int(
1206 view: &ViewHandle,
1207 measure: impl Fn(f64, f64) -> i64 + 'static,
1208 action: impl Fn(i64, i64) + 'static,
1209) -> ViewHandle {
1210 unsafe {
1211 let meta = resolve::int_meta();
1212 let wt = resolve::int_equatable_wt();
1213 let of = Box::leak(Box::new(abi::IndexClosureCtx::new(
1214 emit_measured_int,
1215 boxed_ctx(Box::new(measure) as Box<dyn Fn(f64, f64) -> i64>),
1216 ))) as *mut abi::IndexClosureCtx;
1217 let act = pair_words(invoke_int_pair, boxed_ctx(Box::new(action) as Box<dyn Fn(i64, i64)>));
1218 generic1_modifier_ints(
1219 resolve::on_geometry_change_fn(),
1220 resolve::on_geometry_change_oret(),
1221 view,
1222 meta,
1223 wt,
1224 &[
1225 meta as u64,
1226 abi::index_closure_thunk as *const () as u64,
1227 of as u64,
1228 act[0],
1229 act[1],
1230 ],
1231 [0.0; 4],
1232 )
1233 }
1234}
1235
1236pub fn on_geometry_change_f64(
1242 view: &ViewHandle,
1243 measure: impl Fn(f64, f64) -> f64 + 'static,
1244 action: impl Fn(f64, f64) + 'static,
1245) -> ViewHandle {
1246 unsafe {
1247 let meta = resolve::double_meta();
1248 let wt = resolve::double_equatable_wt();
1249 let of = Box::leak(Box::new(abi::IndexClosureCtx::new(
1250 emit_measured_f64,
1251 boxed_ctx(Box::new(measure) as Box<dyn Fn(f64, f64) -> f64>),
1252 ))) as *mut abi::IndexClosureCtx;
1253 let act = pair_words(invoke_f64_pair, boxed_ctx(Box::new(action) as Box<dyn Fn(f64, f64)>));
1254 generic1_modifier_ints(
1255 resolve::on_geometry_change_fn(),
1256 resolve::on_geometry_change_oret(),
1257 view,
1258 meta,
1259 wt,
1260 &[
1261 meta as u64,
1262 abi::index_closure_thunk as *const () as u64,
1263 of as u64,
1264 act[0],
1265 act[1],
1266 ],
1267 [0.0; 4],
1268 )
1269 }
1270}
1271
1272unsafe fn observable_object() -> (u64, *const c_void, *const c_void) {
1284 unsafe {
1285 let conformance = crate::conformance::observable_object();
1286 let meta = abi::metadata0(resolve::json_decoder_ma());
1287 assert!(!meta.is_null(), "JSONDecoder metadata");
1288 let object = abi::call_regs(resolve::json_decoder_init(), abi::ints(&[]), [0.0; 4], meta as u64)[0];
1290 (object, meta, conformance.witness_table())
1291 }
1292}
1293
1294unsafe fn object_modifier(
1296 func: *const c_void,
1297 oret: *const c_void,
1298 view: &ViewHandle,
1299) -> ViewHandle {
1300 unsafe {
1301 let (object, meta, wt) = observable_object();
1302 generic1_modifier_ints(func, oret, view, meta, wt, &[object], [0.0; 4])
1305 }
1306}
1307
1308pub fn environment_object(view: &ViewHandle) -> ViewHandle {
1310 unsafe {
1311 object_modifier(resolve::environment_object_fn(), resolve::environment_object_oret(), view)
1312 }
1313}
1314
1315pub fn focused_object(view: &ViewHandle) -> ViewHandle {
1317 unsafe {
1318 object_modifier(resolve::focused_object_fn(), resolve::focused_object_oret(), view)
1319 }
1320}
1321
1322pub fn focused_scene_object(view: &ViewHandle) -> ViewHandle {
1324 unsafe {
1325 object_modifier(
1326 resolve::focused_scene_object_fn(),
1327 resolve::focused_scene_object_oret(),
1328 view,
1329 )
1330 }
1331}
1332
1333pub fn accessibility_default_focus(view: &ViewHandle, value: bool) -> ViewHandle {
1339 unsafe {
1340 let mut binding = accessibility_focus_binding();
1341 let mut v = u64::from(value);
1342 generic1_modifier_ints(
1343 resolve::a11y_default_focus_fn(),
1344 resolve::a11y_default_focus_oret(),
1345 view,
1346 resolve::bool_meta(),
1347 resolve::bool_hashable_wt(),
1348 &[binding.as_mut_ptr() as u64, &mut v as *mut u64 as u64],
1349 [0.0; 4],
1350 )
1351 }
1352}
1353
1354pub(crate) unsafe fn accessibility_focus_binding() -> [u64; 3] {
1356 unsafe {
1357 focus_binding(
1358 mangle::bound(&swift_type!(SwiftUI.AccessibilityFocusState), &[mangle::BOOL]).as_bytes(),
1359 resolve::a11y_focus_state_bool_init(),
1360 resolve::a11y_focus_state_projected(),
1361 )
1362 }
1363}
1364
1365
1366pub fn layout_value_int(view: &ViewHandle, value: i64) -> ViewHandle {
1371 unsafe {
1372 let wt = crate::conformance::layout_value_key().witness_table();
1373 let meta = resolve::divider_meta();
1374 let mut boxed = value;
1375 generic1_modifier_ints(
1376 resolve::layout_value_fn(),
1377 resolve::layout_value_oret(),
1378 view,
1379 meta,
1380 wt,
1381 &[meta as u64, &mut boxed as *mut i64 as u64],
1382 [0.0; 4],
1383 )
1384 }
1385}
1386
1387unsafe fn synthesized_value_modifier(
1390 func: *const c_void,
1391 oret: *const c_void,
1392 view: &ViewHandle,
1393 wt: *const c_void,
1394) -> ViewHandle {
1395 unsafe {
1396 let mut empty = 0u64;
1400 generic1_modifier_ints(
1401 func,
1402 oret,
1403 view,
1404 resolve::empty_meta(),
1405 wt,
1406 &[&mut empty as *mut u64 as u64],
1407 [0.0; 4],
1408 )
1409 }
1410}
1411
1412pub fn presentation_sizing(view: &ViewHandle) -> ViewHandle {
1414 unsafe {
1415 synthesized_value_modifier(
1416 resolve::presentation_sizing_fn(),
1417 resolve::presentation_sizing_oret(),
1418 view,
1419 crate::conformance::presentation_sizing().witness_table(),
1420 )
1421 }
1422}
1423
1424pub fn preview_context(view: &ViewHandle) -> ViewHandle {
1426 unsafe {
1427 synthesized_value_modifier(
1428 resolve::preview_context_fn(),
1429 resolve::preview_context_oret(),
1430 view,
1431 crate::conformance::preview_context().witness_table(),
1432 )
1433 }
1434}
1435
1436unsafe fn image_scale_key_path() -> u64 {
1450 use std::sync::atomic::{AtomicU64, Ordering};
1451 static CACHE: AtomicU64 = AtomicU64::new(0);
1452 unsafe {
1453 let cached = CACHE.load(Ordering::Relaxed);
1454 if cached != 0 {
1455 return cached;
1456 }
1457 let meta = abi::metadata0(resolve::image_scale_ma());
1458 let mut arg = vec![0u8; abi::type_bytes(meta)];
1459 abi::inject_enum_tag(&mut arg, meta, 2);
1460 let host = super::empty_view();
1465 let subst = [host.meta, host.wt];
1466 let (_, _, size) = opaque_result_type(resolve::image_scale_oret(), &subst);
1467 let r = abi::call_modifier(
1468 resolve::image_scale_fn(),
1469 host.value_ptr(),
1470 host.meta,
1471 host.wt,
1472 &[arg.as_ptr() as u64],
1473 [0.0; 4],
1474 size,
1475 );
1476 let kp = u64::from_le_bytes(r[0..8].try_into().expect("key path word"));
1477 swift_runtime_sys::RuntimeRaw::swift_retain(kp as *mut c_void);
1479 CACHE.store(kp, Ordering::Relaxed);
1480 kp
1481 }
1482}
1483
1484unsafe fn symbol_variants_key_path() -> u64 {
1494 use std::sync::atomic::{AtomicU64, Ordering};
1495 static CACHE: AtomicU64 = AtomicU64::new(0);
1496 unsafe {
1497 let cached = CACHE.load(Ordering::Relaxed);
1498 if cached != 0 {
1499 return cached;
1500 }
1501 let meta = abi::metadata0(resolve::symbol_variants_ma());
1502 let mut arg = super::static_getter_indirect(
1503 resolve::require(super::SymbolVariants::Fill.getter()),
1504 meta,
1505 0,
1506 );
1507 let host = super::empty_view();
1512 let subst = [host.meta, host.wt];
1513 let (_, _, size) = opaque_result_type(resolve::symbol_variant_oret(), &subst);
1514 let r = abi::call_modifier(
1515 resolve::symbol_variant_fn(),
1516 host.value_ptr(),
1517 host.meta,
1518 host.wt,
1519 &[arg.as_mut_ptr() as u64],
1520 [0.0; 4],
1521 size,
1522 );
1523 let kp = u64::from_le_bytes(r[0..8].try_into().expect("key path word"));
1526 swift_runtime_sys::RuntimeRaw::swift_retain(kp as *mut c_void);
1527 CACHE.store(kp, Ordering::Relaxed);
1528 kp
1529 }
1530}
1531
1532pub fn environment_symbol_variants(view: &ViewHandle, value: super::SymbolVariants) -> ViewHandle {
1534 unsafe {
1535 let meta = abi::metadata0(resolve::symbol_variants_ma());
1536 let arg = super::static_getter_indirect(value.resolved(), meta, 0);
1537 generic1_modifier_ints(
1538 resolve::environment_fn(),
1539 resolve::environment_oret(),
1540 view,
1541 meta,
1542 core::ptr::null(),
1543 &[symbol_variants_key_path(), arg.as_ptr() as u64],
1544 [0.0; 4],
1545 )
1546 }
1547}
1548
1549pub fn environment_image_scale(view: &ViewHandle, scale: ImageScale) -> ViewHandle {
1554 unsafe {
1555 let meta = abi::metadata0(resolve::image_scale_ma());
1556 let mut value = vec![0u8; abi::type_bytes(meta)];
1557 abi::inject_enum_tag(&mut value, meta, scale.tag());
1558 generic1_modifier_ints(
1559 resolve::environment_fn(),
1560 resolve::environment_oret(),
1561 view,
1562 meta,
1563 core::ptr::null(),
1564 &[image_scale_key_path(), value.as_ptr() as u64],
1565 [0.0; 4],
1566 )
1567 }
1568}
1569
1570pub(crate) unsafe extern "C" fn transform_image_scale(
1577 a0: u64,
1578 _a1: u64,
1579 _a2: u64,
1580 payload: *mut c_void,
1581) {
1582 unsafe {
1583 let f = boxed_ref::<dyn Fn(ImageScale) -> ImageScale>(payload);
1584 let meta = abi::metadata0(resolve::image_scale_ma());
1585 let p = a0 as *mut u8;
1586 let current = match abi::read_enum_tag(p, meta) {
1587 0 => ImageScale::Small,
1588 1 => ImageScale::Medium,
1589 _ => ImageScale::Large,
1590 };
1591 let next = f(current);
1592 let size = abi::type_bytes(meta).max(1);
1593 abi::inject_enum_tag(core::slice::from_raw_parts_mut(p, size), meta, next as u32);
1594 }
1595}
1596
1597pub fn transform_environment_image_scale(
1599 view: &ViewHandle,
1600 action: impl Fn(ImageScale) -> ImageScale + 'static,
1601) -> ViewHandle {
1602 unsafe {
1603 let meta = abi::metadata0(resolve::image_scale_ma());
1604 let c = wide_words(
1605 transform_image_scale,
1606 boxed_ctx(Box::new(action) as Box<dyn Fn(ImageScale) -> ImageScale>),
1607 );
1608 generic1_modifier_ints(
1609 resolve::transform_environment_fn(),
1610 resolve::transform_environment_oret(),
1611 view,
1612 meta,
1613 core::ptr::null(),
1614 &[image_scale_key_path(), c[0], c[1]],
1615 [0.0; 4],
1616 )
1617 }
1618}
1619
1620struct TransformCtx {
1622 step: Box<dyn Fn(u8) -> u8>,
1624}
1625
1626unsafe extern "C" fn transform_env_byte(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
1637 unsafe {
1638 let ctx = &*(payload as *const TransformCtx);
1639 let p = a0 as *mut u8;
1640 *p = (ctx.step)(*p);
1641 }
1642}
1643
1644pub fn transform_environment<K: crate::state::EnvKey>(
1662 view: &ViewHandle,
1663 action: impl Fn(K::Value) -> K::Value + 'static,
1664) -> ViewHandle {
1665 unsafe {
1666 assert_eq!(
1667 K::buffer_bytes(),
1668 1,
1669 "transform_environment handles one-byte values; this key's is wider"
1670 );
1671 let meta = abi::resolve_type_by_mangled_name(K::value_type());
1672 assert!(!meta.is_null(), "environment value metadata");
1673 let ctx = Box::leak(Box::new(TransformCtx {
1674 step: Box::new(move |b| K::encode(&action(K::decode(&[b])))),
1675 })) as *mut TransformCtx;
1676 let c = wide_words(transform_env_byte, ctx as *mut c_void);
1677 generic1_modifier_ints(
1678 resolve::transform_environment_fn(),
1679 resolve::transform_environment_oret(),
1680 view,
1681 meta,
1682 core::ptr::null(),
1683 &[K::key_path(), c[0], c[1]],
1684 [0.0; 4],
1685 )
1686 }
1687}
1688
1689pub fn drag_container_selection(view: &ViewHandle, selected: &[&str]) -> ViewHandle {
1691 unsafe {
1692 let meta = resolve::string_meta();
1693 let wt = resolve::string_hashable_wt();
1694 let selection = word_closure(emit_strings, strings_payload(swift_string_array(selected)));
1695 let ns = crate::type_by_name!(swift_type!(SwiftUI.Namespace.ID).as_bytes());
1696 let opt = crate::type_by_name!(swift_type!(opt SwiftUI.Namespace.ID).as_bytes());
1697 let mut buf = vec![0u8; abi::type_bytes(opt)];
1698 abi::store_optional_tag(&mut buf, ns, false);
1699 let mut ints = vec![selection[0], selection[1]];
1700 ints.extend((0..abi::type_bytes(opt) / 8).map(|i| {
1701 u64::from_le_bytes(buf[i * 8..i * 8 + 8].try_into().expect("word"))
1702 }));
1703 generic1_modifier_ints(
1704 resolve::drag_container_selection_fn(),
1705 resolve::drag_container_selection_oret(),
1706 view,
1707 meta,
1708 wt,
1709 &ints,
1710 [0.0; 4],
1711 )
1712 }
1713}
1714
1715#[derive(Clone, Copy, Debug, Default, PartialEq)]
1728pub struct ScrollGeometry {
1729 pub content_offset: (f64, f64),
1731 pub content_size: (f64, f64),
1733 pub content_insets: (f64, f64, f64, f64),
1735 pub container_size: (f64, f64),
1737 pub visible_rect: (f64, f64, f64, f64),
1739}
1740
1741fn scroll_geometry_offsets() -> Option<&'static [usize; 5]> {
1743 static CACHE: std::sync::OnceLock<Option<[usize; 5]>> = std::sync::OnceLock::new();
1744 CACHE
1745 .get_or_init(|| unsafe {
1746 let offs = abi::struct_field_offsets(abi::metadata0(resolve::scroll_geometry_ma()))?;
1747 (offs.len() == 5).then(|| [offs[0], offs[1], offs[2], offs[3], offs[4]])
1748 })
1749 .as_ref()
1750}
1751
1752pub(crate) unsafe fn read_scroll_geometry(p: *const u8) -> ScrollGeometry {
1757 let Some(&[offset, size, insets, container, visible]) = scroll_geometry_offsets() else {
1758 return ScrollGeometry::default();
1759 };
1760 unsafe {
1761 let at = |o: usize| *(p.add(o) as *const f64);
1762 let pair = |o: usize| (at(o), at(o + 8));
1763 ScrollGeometry {
1764 content_offset: pair(offset),
1765 content_size: pair(size),
1766 content_insets: (at(insets), at(insets + 8), at(insets + 16), at(insets + 24)),
1767 container_size: pair(container),
1768 visible_rect: (at(visible), at(visible + 8), at(visible + 16), at(visible + 24)),
1769 }
1770 }
1771}
1772
1773pub(crate) unsafe extern "C" fn emit_scroll_measure(out: *mut u8, geometry: i64, payload: *mut c_void) {
1779 unsafe {
1780 let f = boxed_ref::<dyn Fn(ScrollGeometry) -> i64>(payload);
1781 *(out as *mut i64) = f(read_scroll_geometry(geometry as *const u8));
1782 }
1783}
1784
1785pub fn on_scroll_geometry_change(
1787 view: &ViewHandle,
1788 measure: impl Fn(ScrollGeometry) -> i64 + 'static,
1789 action: impl Fn(i64, i64) + 'static,
1790) -> ViewHandle {
1791 unsafe {
1792 let meta = resolve::int_meta();
1793 let wt = resolve::int_equatable_wt();
1794 let of = Box::leak(Box::new(abi::IndexClosureCtx::new(
1795 emit_scroll_measure,
1796 boxed_ctx(Box::new(measure) as Box<dyn Fn(ScrollGeometry) -> i64>),
1797 ))) as *mut abi::IndexClosureCtx;
1798 let act = pair_words(invoke_int_pair, boxed_ctx(Box::new(action) as Box<dyn Fn(i64, i64)>));
1799 generic1_modifier_ints(
1800 resolve::on_scroll_geometry_change_fn(),
1801 resolve::on_scroll_geometry_change_oret(),
1802 view,
1803 meta,
1804 wt,
1805 &[
1806 meta as u64,
1807 abi::index_closure_thunk as *const () as u64,
1808 of as u64,
1809 act[0],
1810 act[1],
1811 ],
1812 [0.0; 4],
1813 )
1814 }
1815}
1816
1817pub fn on_scroll_geometry_change_f64(
1819 view: &ViewHandle,
1820 measure: impl Fn(ScrollGeometry) -> f64 + 'static,
1821 action: impl Fn(f64, f64) + 'static,
1822) -> ViewHandle {
1823 unsafe {
1824 let meta = resolve::double_meta();
1825 let wt = resolve::double_equatable_wt();
1826 let of = Box::leak(Box::new(abi::IndexClosureCtx::new(
1827 emit_scroll_measure_f64,
1828 boxed_ctx(Box::new(measure) as Box<dyn Fn(ScrollGeometry) -> f64>),
1829 ))) as *mut abi::IndexClosureCtx;
1830 let act = pair_words(invoke_f64_pair, boxed_ctx(Box::new(action) as Box<dyn Fn(f64, f64)>));
1831 generic1_modifier_ints(
1832 resolve::on_scroll_geometry_change_fn(),
1833 resolve::on_scroll_geometry_change_oret(),
1834 view,
1835 meta,
1836 wt,
1837 &[
1838 meta as u64,
1839 abi::index_closure_thunk as *const () as u64,
1840 of as u64,
1841 act[0],
1842 act[1],
1843 ],
1844 [0.0; 4],
1845 )
1846 }
1847}
1848
1849pub(crate) unsafe extern "C" fn emit_scroll_measure_f64(
1852 out: *mut u8,
1853 geometry: i64,
1854 payload: *mut c_void,
1855) {
1856 unsafe {
1857 let f = boxed_ref::<dyn Fn(ScrollGeometry) -> f64>(payload);
1858 *(out as *mut f64) = f(read_scroll_geometry(geometry as *const u8));
1859 }
1860}
1861
1862#[derive(Clone, Copy, Debug, Default, PartialEq)]
1875pub struct ViewDimensions {
1876 pub width: f64,
1878 pub height: f64,
1880}
1881
1882pub(crate) unsafe fn read_view_dimensions(p: *const u8) -> ViewDimensions {
1887 unsafe {
1888 let get = |f: *const c_void| {
1889 let out: f64;
1890 core::arch::asm!("blr {f}", f = in(reg) f,
1893 in("x20") p,
1894 lateout("d0") out,
1895 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
1896 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1897 lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
1898 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
1899 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
1900 clobber_abi("C"),
1901 );
1902 out
1903 };
1904 let d = ViewDimensions {
1905 width: get(resolve::view_dimensions_width()),
1906 height: get(resolve::view_dimensions_height()),
1907 };
1908 debug_assert_eq!(Some((d.width, d.height)), stored_view_size(p));
1913 d
1914 }
1915}
1916
1917unsafe fn stored_view_size(p: *const u8) -> Option<(f64, f64)> {
1925 unsafe {
1926 let offs = abi::struct_field_offsets(abi::metadata0(resolve::view_dimensions_ma()))?;
1927 let &[_, size] = offs.as_slice() else { return None };
1929 let at = p.add(size) as *const f64;
1930 Some((*at, *at.add(1)))
1931 }
1932}
1933
1934pub(crate) unsafe extern "C" fn compute_length(arg: u64, payload: *mut c_void) -> f64 {
1936 unsafe {
1937 let f = boxed_ref::<dyn Fn(ViewDimensions) -> f64>(payload);
1938 f(read_view_dimensions(arg as *const u8))
1939 }
1940}
1941
1942pub fn alignment_guide(
1947 view: &ViewHandle,
1948 alignment: HorizontalAlignment,
1949 compute: impl Fn(ViewDimensions) -> f64 + 'static,
1950) -> ViewHandle {
1951 unsafe {
1952 let meta = abi::metadata0(resolve::horizontal_alignment_ma());
1953 let a = frozen_static(meta, alignment.getter());
1954 let ctx = Box::leak(Box::new(abi::FloatClosureCtx::new(
1955 compute_length,
1956 boxed_ctx(Box::new(compute) as Box<dyn Fn(ViewDimensions) -> f64>),
1957 ))) as *mut abi::FloatClosureCtx;
1958 opaque_modifier(
1959 resolve::alignment_guide_fn(),
1960 resolve::alignment_guide_oret(),
1961 view,
1962 &[a, abi::float_closure_thunk as *const () as u64, ctx as u64],
1963 [0.0; 4],
1964 )
1965 }
1966}
1967
1968unsafe fn generic_pair_modifier(
1974 func: *const c_void,
1975 oret: *const c_void,
1976 view: &ViewHandle,
1977 first: (*const c_void, *const c_void),
1978 second: (*const c_void, *const c_void),
1979 leading: &[u64],
1980) -> ViewHandle {
1981 unsafe {
1982 let subst = [
1983 view.meta,
1984 first.0,
1985 second.0,
1986 view.wt,
1987 first.1,
1988 second.1,
1989 ];
1990 let (meta, wt, size) = opaque_result_type(oret, &subst);
1991 let mut ints = leading.to_vec();
1992 ints.extend(subst.iter().map(|p| *p as u64));
1993 let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, [0.0; 4], size);
1994 wrap_owned(r, meta, wt)
1995 }
1996}
1997
1998pub fn background_preference_value(
2000 view: &ViewHandle,
2001 alignment: Alignment,
2002 content: &ViewHandle,
2003) -> ViewHandle {
2004 unsafe { preference_value_modifier(
2005 resolve::background_preference_value_fn(),
2006 resolve::background_preference_value_oret(),
2007 view, alignment, content,
2008 ) }
2009}
2010
2011pub fn overlay_preference_value(
2013 view: &ViewHandle,
2014 alignment: Alignment,
2015 content: &ViewHandle,
2016) -> ViewHandle {
2017 unsafe { preference_value_modifier(
2018 resolve::overlay_preference_value_fn(),
2019 resolve::overlay_preference_value_oret(),
2020 view, alignment, content,
2021 ) }
2022}
2023
2024unsafe fn preference_value_modifier(
2025 func: *const c_void,
2026 oret: *const c_void,
2027 view: &ViewHandle,
2028 alignment: Alignment,
2029 content: &ViewHandle,
2030) -> ViewHandle {
2031 unsafe {
2032 let key_meta = resolve::spacer_meta();
2033 let key_wt = crate::conformance::preference_key().witness_table();
2034 let a = alignment.words();
2035 let c = view_words(content);
2036 generic_pair_modifier(
2037 func,
2038 oret,
2039 view,
2040 (key_meta, key_wt),
2041 (content.meta, content.wt),
2042 &[key_meta as u64, a[0], a[1], c[0], c[1]],
2043 )
2044 }
2045}
2046
2047unsafe extern "C" fn saw_int_array(value: *const u8, payload: *mut c_void) {
2053 unsafe {
2054 let f = boxed_ref::<dyn Fn(&[i64])>(payload);
2055 let (base, count) = abi::array_parts(value as u64);
2056 f(core::slice::from_raw_parts(base as *const i64, count));
2057 }
2058}
2059
2060pub fn on_scroll_target_visibility_change(
2065 view: &ViewHandle,
2066 threshold: f64,
2067 visible: impl Fn(&[i64]) + 'static,
2068) -> ViewHandle {
2069 unsafe {
2070 let meta = resolve::int_meta();
2071 let wt = resolve::int_hashable_wt();
2072 let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
2073 saw_int_array,
2074 boxed_ctx(Box::new(visible) as Box<dyn Fn(&[i64])>),
2075 ))) as *mut abi::ValueClosureCtx;
2076 generic1_modifier_ints(
2077 resolve::scroll_target_visibility_fn(),
2078 resolve::scroll_target_visibility_oret(),
2079 view,
2080 meta,
2081 wt,
2082 &[meta as u64, abi::value_closure_thunk as *const () as u64, ctx as u64],
2083 [threshold, 0.0, 0.0, 0.0],
2084 )
2085 }
2086}
2087
2088unsafe extern "C" fn identity_effect(_out: *mut u8, _payload: *mut c_void) {}
2094
2095pub fn visual_effect(view: &ViewHandle) -> ViewHandle {
2101 unsafe {
2102 let meta = crate::type_by_name!(swift_type!(SwiftUI.EmptyVisualEffect).as_bytes());
2103 let wt = resolve::empty_visual_effect_wt();
2104 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
2105 identity_effect,
2106 core::ptr::null_mut(),
2107 ))) as *mut abi::ClosureCtx;
2108 generic1_modifier_ints(
2109 resolve::visual_effect_fn(),
2110 resolve::visual_effect_oret(),
2111 view,
2112 meta,
2113 wt,
2114 &[abi::swift_closure_thunk as *const () as u64, ctx as u64],
2115 [0.0; 4],
2116 )
2117 }
2118}
2119
2120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2124pub enum Axis {
2125 Horizontal = 0,
2126 Vertical = 1,
2127}
2128
2129#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2131pub enum ScrollTransitionConfiguration {
2132 Interactive,
2134 Animated,
2136 Identity,
2138}
2139
2140impl ScrollTransitionConfiguration {
2141 fn getter(self) -> *const c_void {
2142 match self {
2143 ScrollTransitionConfiguration::Interactive => resolve::scroll_transition_config_interactive(),
2144 ScrollTransitionConfiguration::Animated => resolve::scroll_transition_config_animated(),
2145 ScrollTransitionConfiguration::Identity => resolve::scroll_transition_config_identity(),
2146 }
2147 }
2148}
2149
2150unsafe fn scroll_transition_tail(axis: Option<Axis>) -> (Vec<u64>, *const c_void, *const c_void) {
2160 unsafe {
2161 let axis_meta = abi::metadata0(resolve::axis_ma());
2162 let opt_axis = crate::type_by_name!(swift_type!(opt enum SwiftUI.Axis).as_bytes());
2163 let tag = axis.map(|a| a as u32);
2164 let mut payload = vec![0u8; abi::type_bytes(axis_meta).max(1)];
2165 if let Some(t) = tag {
2166 abi::inject_enum_tag(&mut payload, axis_meta, t);
2167 }
2168 let words = optional_words(axis_meta, opt_axis, &payload, tag.is_some());
2169 let meta = crate::type_by_name!(swift_type!(SwiftUI.EmptyVisualEffect).as_bytes());
2170 let wt = resolve::empty_visual_effect_wt();
2171 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
2172 identity_effect,
2173 core::ptr::null_mut(),
2174 ))) as *mut abi::ClosureCtx;
2175 let mut ints = words;
2176 ints.push(abi::swift_closure_thunk as *const () as u64);
2177 ints.push(ctx as u64);
2178 (ints, meta, wt)
2179 }
2180}
2181
2182pub fn scroll_transition(
2184 view: &ViewHandle,
2185 config: ScrollTransitionConfiguration,
2186 axis: Option<Axis>,
2187) -> ViewHandle {
2188 unsafe {
2189 let cfg_meta = abi::metadata0(resolve::scroll_transition_config_ma());
2190 let cfg = abi::static_out(config.getter(), cfg_meta, abi::type_bytes(cfg_meta));
2191 let (tail, meta, wt) = scroll_transition_tail(axis);
2192 let mut ints = vec![cfg.as_ptr() as u64];
2193 ints.extend_from_slice(&tail);
2194 generic1_modifier_ints(
2195 resolve::scroll_transition_fn(),
2196 resolve::scroll_transition_oret(),
2197 view,
2198 meta,
2199 wt,
2200 &ints,
2201 [0.0; 4],
2202 )
2203 }
2204}
2205
2206pub fn scroll_transition_ends(
2216 view: &ViewHandle,
2217 top_leading: ScrollTransitionConfiguration,
2218 bottom_trailing: ScrollTransitionConfiguration,
2219 axis: Option<Axis>,
2220) -> ViewHandle {
2221 unsafe {
2222 let cfg_meta = abi::metadata0(resolve::scroll_transition_config_ma());
2223 let size = abi::type_bytes(cfg_meta);
2224 let lead = abi::static_out(top_leading.getter(), cfg_meta, size);
2225 let trail = abi::static_out(bottom_trailing.getter(), cfg_meta, size);
2226 let (tail, meta, wt) = scroll_transition_tail(axis);
2227 let mut ints = vec![lead.as_ptr() as u64, trail.as_ptr() as u64];
2228 ints.extend_from_slice(&tail);
2229 generic1_modifier_ints(
2230 resolve::scroll_transition_ends_fn(),
2231 resolve::scroll_transition_ends_oret(),
2232 view,
2233 meta,
2234 wt,
2235 &ints,
2236 [0.0; 4],
2237 )
2238 }
2239}
2240
2241unsafe fn bool_trait(
2247 key_ma: *const c_void,
2248 key_wt: *const c_void,
2249 view: &ViewHandle,
2250 value: bool,
2251) -> ViewHandle {
2252 unsafe {
2253 let meta = abi::metadata0(key_ma);
2254 let byte = Box::leak(Box::new(u8::from(value))) as *mut u8;
2255 generic1_modifier_ints(
2256 resolve::view_trait_fn(),
2257 resolve::view_trait_oret(),
2258 view,
2259 meta,
2260 key_wt,
2261 &[meta as u64, byte as u64],
2262 [0.0; 4],
2263 )
2264 }
2265}
2266
2267pub fn selection_disabled(view: &ViewHandle, disabled: bool) -> ViewHandle {
2275 unsafe {
2276 bool_trait(
2277 resolve::is_selection_enabled_key_ma(),
2278 resolve::is_selection_enabled_key_wt(),
2279 view,
2280 !disabled,
2281 )
2282 }
2283}
2284
2285unsafe extern "C" fn emit_anchor_value(out: *mut u8, payload: *mut c_void) {
2291 unsafe {
2292 let f = boxed_ref::<dyn Fn() -> i64>(payload);
2293 *(out as *mut i64) = f();
2294 }
2295}
2296
2297unsafe extern "C" fn fold_anchor_value(value: *const u8, _anchor: *const u8, payload: *mut c_void) {
2299 unsafe {
2300 let f = boxed_ref::<dyn Fn(i64) -> i64>(payload);
2301 let slot = value as *mut i64;
2302 *slot = f(*slot);
2303 }
2304}
2305
2306unsafe fn anchor_preference_parts() -> (*const c_void, *const c_void, *const c_void, u64) {
2313 unsafe {
2314 let key_meta = resolve::spacer_meta();
2315 let key_wt = crate::conformance::preference_key().witness_table();
2316 let rect_meta = crate::type_by_name!(swift_type!(objc struct CGRect).as_bytes());
2317 assert!(!rect_meta.is_null(), "CGRect metadata");
2318 let source: u64;
2319 core::arch::asm!("blr {f}", f = in(reg) resolve::anchor_source_bounds(),
2320 lateout("x0") source,
2321 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2322 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
2323 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2324 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2325 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
2326 clobber_abi("C"),
2327 );
2328 (key_meta, key_wt, rect_meta, source)
2329 }
2330}
2331
2332unsafe fn anchor_modifier(
2334 func: *const c_void,
2335 oret: *const c_void,
2336 view: &ViewHandle,
2337 closure: [u64; 2],
2338) -> ViewHandle {
2339 unsafe {
2340 let (key_meta, key_wt, rect_meta, source) = anchor_preference_parts();
2341 let av = view.meta;
2342 let aw = view.wt;
2343 let subst = [av, rect_meta, key_meta, aw, key_wt];
2344 let (meta, wt, size) = opaque_result_type(oret, &subst);
2345 let ints = [
2346 key_meta as u64,
2347 source,
2348 closure[0],
2349 closure[1],
2350 av as u64,
2351 rect_meta as u64,
2352 key_meta as u64,
2353 aw as u64,
2354 key_wt as u64,
2355 ];
2356 let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, [0.0; 4], size);
2357 wrap_owned(r, meta, wt)
2358 }
2359}
2360
2361pub fn anchor_preference(view: &ViewHandle, transform: impl Fn() -> i64 + 'static) -> ViewHandle {
2366 unsafe {
2367 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
2368 emit_anchor_value,
2369 boxed_ctx(Box::new(transform) as Box<dyn Fn() -> i64>),
2370 ))) as *mut abi::ClosureCtx;
2371 anchor_modifier(
2372 resolve::anchor_preference_fn(),
2373 resolve::anchor_preference_oret(),
2374 view,
2375 [abi::swift_closure_thunk as *const () as u64, ctx as u64],
2376 )
2377 }
2378}
2379
2380pub fn transform_anchor_preference(view: &ViewHandle, transform: impl Fn(i64) -> i64 + 'static) -> ViewHandle {
2385 unsafe {
2386 anchor_modifier(
2387 resolve::transform_anchor_preference_fn(),
2388 resolve::transform_anchor_preference_oret(),
2389 view,
2390 pair_words(fold_anchor_value, boxed_ctx(Box::new(transform) as Box<dyn Fn(i64) -> i64>)),
2391 )
2392 }
2393}
2394
2395swift_statics! {
2396 pub enum DefaultFocusPriority {
2398 Automatic = c"$s7SwiftUI30DefaultFocusEvaluationPriorityV9automaticACvgZ",
2399 UserInitiated = c"$s7SwiftUI30DefaultFocusEvaluationPriorityV13userInitiatedACvgZ",
2400 }
2401}
2402
2403pub fn default_focus(
2410 view: &ViewHandle,
2411 value: bool,
2412 priority: DefaultFocusPriority,
2413) -> ViewHandle {
2414 unsafe {
2415 let b = focus_binding(
2416 mangle::bound(&swift_type!(SwiftUI.FocusState), &[mangle::BOOL]).as_bytes(),
2417 resolve::focus_state_bool_init(),
2418 resolve::focus_state_projected(),
2419 );
2420 let mut binding = [0u8; 24];
2421 for (i, w) in b.iter().enumerate() {
2422 binding[i * 8..i * 8 + 8].copy_from_slice(&w.to_le_bytes());
2423 }
2424 let mut flag = u8::from(value);
2425 let pm = abi::metadata0(resolve::default_focus_priority_ma());
2426 let p = resilient_static(pm, priority.getter());
2427 generic1_modifier_ints(
2428 resolve::default_focus_fn(),
2429 resolve::default_focus_oret(),
2430 view,
2431 resolve::bool_meta(),
2432 resolve::bool_hashable_wt(),
2433 &[
2434 binding.as_ptr() as u64,
2435 &mut flag as *mut u8 as u64,
2436 p.as_ptr() as u64,
2437 ],
2438 [0.0; 4],
2439 )
2440 }
2441}
2442
2443pub fn text_renderer(view: &ViewHandle) -> ViewHandle {
2449 unsafe {
2450 let renderer = abi::metadata0(resolve::edge_insets_ma());
2451 let wt = crate::conformance::text_renderer().witness_table();
2452 let modifier =
2453 abi::generic_metadata2(resolve::text_renderer_modifier_ma(), renderer, wt);
2454 let bytes = vec![0u8; abi::type_bytes(modifier)];
2457 apply_modifier(view, modifier, &bytes)
2458 }
2459}
2460
2461pub fn attributed_text_formatting_definition(view: &ViewHandle) -> ViewHandle {
2467 unsafe {
2468 let meta = abi::metadata0(resolve::swiftui_attributes_ma());
2469 let wt = abi::get_view_wt_for_proto(meta, resolve::attribute_scope_proto());
2470 assert!(!wt.is_null(), "SwiftUIAttributes: AttributeScope");
2471 generic1_modifier_ints(
2472 resolve::attributed_formatting_fn(),
2473 resolve::attributed_formatting_oret(),
2474 view,
2475 meta,
2476 wt,
2477 &[meta as u64],
2478 [0.0; 4],
2479 )
2480 }
2481}
2482
2483swift_statics! {
2484 pub enum ContentTransition {
2486 Identity = c"$s7SwiftUI17ContentTransitionV8identityACvgZ",
2487 Opacity = c"$s7SwiftUI17ContentTransitionV7opacityACvgZ",
2488 Interpolate = c"$s7SwiftUI17ContentTransitionV11interpolateACvgZ",
2489 }
2490}
2491
2492fn content_transition_key_path() -> u64 {
2494 use std::sync::atomic::{AtomicU64, Ordering};
2495 static CACHE: AtomicU64 = AtomicU64::new(0);
2496 static mut SLOT: crate::keypath::Slot = crate::keypath::Slot::new();
2497 let hit = CACHE.load(Ordering::Relaxed);
2498 if hit != 0 {
2499 return hit;
2500 }
2501 unsafe {
2502 let kp = crate::keypath::build(
2503 crate::static_mut(&raw mut SLOT),
2504 crate::keypath::Property {
2505 getter: resolve::env_content_transition_getter(),
2506 setter: resolve::env_content_transition_setter(),
2507 descriptor: resolve::env_content_transition_descriptor(),
2508 root: swift_type!(SwiftUI.EnvironmentValues).as_bytes(),
2509 value: swift_type!(SwiftUI.ContentTransition).as_bytes(),
2510 },
2511 crate::keypath::next_thunks(),
2512 );
2513 CACHE.store(kp, Ordering::Relaxed);
2514 kp
2515 }
2516}
2517
2518pub fn content_transition(view: &ViewHandle, transition: ContentTransition) -> ViewHandle {
2525 unsafe {
2526 let meta = abi::metadata0(resolve::content_transition_ma());
2527 let value = resilient_static(meta, transition.getter());
2528 generic1_modifier_ints(
2529 resolve::environment_fn(),
2530 resolve::environment_oret(),
2531 view,
2532 meta,
2533 core::ptr::null(),
2534 &[content_transition_key_path(), value.as_ptr() as u64],
2535 [0.0; 4],
2536 )
2537 }
2538}
2539
2540unsafe extern "C" fn emit_phase_animation(payload: *mut c_void, _arg: u64) -> u64 {
2550 if !payload.is_null() {
2551 unsafe { swift_runtime_sys::RuntimeRaw::swift_retain(payload) };
2552 }
2553 payload as u64
2554}
2555
2556pub fn phase_animator(
2563 view: &ViewHandle,
2564 phases: &[i64],
2565 content: &ViewHandle,
2566 animated: bool,
2567) -> ViewHandle {
2568 unsafe {
2569 let int_meta = resolve::int_meta();
2570 let mut bytes = Vec::with_capacity(phases.len() * 8);
2571 for p in phases {
2572 bytes.extend_from_slice(&p.to_le_bytes());
2573 }
2574 let array = abi::swift_array(int_meta, phases.len(), &bytes);
2575 let array_meta = abi::resolve_type_by_mangled_name(mangle::array(mangle::INT).as_bytes());
2576 assert!(!array_meta.is_null(), "Array<Int> metadata");
2577 let seq_wt = abi::get_view_wt_for_proto(array_meta, resolve::sequence_proto());
2578 assert!(!seq_wt.is_null(), "Array<Int>: Sequence");
2579 let eq_wt = abi::get_view_wt_for_proto(int_meta, resolve::equatable_proto());
2580 assert!(!eq_wt.is_null(), "Int: Equatable");
2581
2582 let anim = if animated {
2583 let meta = abi::metadata0(resolve::animation_ma());
2584 frozen_static(meta, c"$s7SwiftUI9AnimationV7defaultACvgZ")
2585 } else {
2586 0
2587 };
2588 let a = word_closure(emit_phase_animation, anim as *mut c_void);
2589
2590 let av = content.meta;
2593 let aw = content.wt;
2594 let subst = [view.meta, int_meta, array_meta, av, view.wt, eq_wt, seq_wt, aw];
2595 let (meta, wt, size) = opaque_result_type(resolve::phase_animator_oret(), &subst);
2596 let mut ints = vec![&array as *const u64 as u64];
2597 ints.extend_from_slice(&view_words(content));
2598 ints.extend_from_slice(&a);
2599 ints.extend_from_slice(&[view.meta as u64, int_meta as u64, array_meta as u64, av as u64]);
2600 ints.extend_from_slice(&[view.wt as u64, eq_wt as u64, seq_wt as u64, aw as u64]);
2601 let r = abi::call_modifier_generic(resolve::phase_animator_fn(), view.value_ptr(), &ints, [0.0; 4], size);
2602 wrap_owned(r, meta, wt)
2603 }
2604}
2605
2606pub fn text_input_suggestions(
2614 view: &ViewHandle,
2615 count: usize,
2616 content: &ViewHandle,
2617) -> ViewHandle {
2618 unsafe {
2619 let elem = abi::metadata0(resolve::edge_insets_ma());
2620 let stride = abi::type_bytes(elem);
2621 let bytes = vec![0u8; stride * count];
2622 let array = abi::swift_array(elem, count, &bytes);
2623 let array_meta =
2624 crate::type_by_name!(swift_type!([SwiftUI.EdgeInsets]).as_bytes());
2625 assert!(!array_meta.is_null(), "[EdgeInsets] metadata");
2626 let rac = abi::get_view_wt_for_proto(array_meta, resolve::random_access_collection_proto());
2627 assert!(!rac.is_null(), "[EdgeInsets]: RandomAccessCollection");
2628 let ident = crate::conformance::identifiable().witness_table();
2629
2630 let av = content.meta;
2633 let aw = content.wt;
2634 let subst = [view.meta, array_meta, av, view.wt, rac, aw, ident];
2635 let (meta, wt, size) = opaque_result_type(resolve::text_input_suggestions_oret(), &subst);
2636 let mut ints = vec![&array as *const u64 as u64];
2637 ints.extend_from_slice(&view_words(content));
2638 ints.extend_from_slice(&[view.meta as u64, array_meta as u64, av as u64]);
2639 ints.extend_from_slice(&[view.wt as u64, rac as u64, aw as u64, ident as u64]);
2640 let r = abi::call_modifier_generic(resolve::text_input_suggestions_fn(), view.value_ptr(), &ints, [0.0; 4], size);
2641 wrap_owned(r, meta, wt)
2642 }
2643}
2644
2645unsafe extern "C" fn emit_drag_items(out: *mut u8, payload: *mut c_void) {
2647 unsafe { *(out as *mut u64) = payload as u64 };
2648}
2649
2650pub fn drag_container(view: &ViewHandle, items: &[&str]) -> ViewHandle {
2657 unsafe {
2658 let item = resolve::string_meta();
2659 let transferable = abi::get_view_wt_for_proto(item, resolve::transferable_proto());
2660 assert!(!transferable.is_null(), "String: Transferable");
2661 let identifiable = crate::conformance::string_identifiable().witness_table();
2662 let array = swift_string_array(items);
2663 let array_meta = abi::resolve_type_by_mangled_name(mangle::array(mangle::STRING).as_bytes());
2664 assert!(!array_meta.is_null(), "[String] metadata");
2665 let collection = abi::get_view_wt_for_proto(array_meta, resolve::collection_proto());
2666 assert!(!collection.is_null(), "[String]: Collection");
2667
2668 let ns = abi::metadata0(resolve::namespace_id_ma());
2670 let opt = crate::type_by_name!(swift_type!(opt SwiftUI.Namespace.ID).as_bytes());
2671 assert!(!opt.is_null(), "Optional<Namespace.ID>");
2672 let mut none = vec![0u8; abi::type_bytes(opt)];
2673 abi::store_optional_tag(&mut none, ns, false);
2674 let none_words = optional_words(ns, opt, &[], false);
2675
2676 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
2677 emit_drag_items,
2678 array as *mut c_void,
2679 ))) as *mut abi::ClosureCtx;
2680
2681 let av = view.meta;
2682 let aw = view.wt;
2683 let subst = [av, item, array_meta, aw, transferable, identifiable, collection];
2684 let (meta, wt, size) = opaque_result_type(resolve::drag_container_oret(), &subst);
2685 let mut ints = vec![item as u64];
2686 ints.extend_from_slice(&none_words);
2687 ints.push(abi::swift_closure_thunk as *const () as u64);
2688 ints.push(ctx as u64);
2689 ints.extend_from_slice(&[av as u64, item as u64, array_meta as u64]);
2690 ints.extend_from_slice(&[aw as u64, transferable as u64, identifiable as u64, collection as u64]);
2691 let r = abi::call_modifier_generic(resolve::drag_container_fn(), view.value_ptr(), &ints, [0.0; 4], size);
2692 wrap_owned(r, meta, wt)
2693 }
2694}
2695
2696pub(crate) unsafe extern "C" fn saw_published_int(value: *const u8, payload: *mut c_void) {
2698 unsafe {
2699 let f = boxed_ref::<dyn Fn(i64)>(payload);
2700 f(*(value as *const i64));
2701 }
2702}
2703
2704pub fn on_receive(view: &ViewHandle, complete_immediately: bool, perform: impl Fn(i64) + 'static) -> ViewHandle {
2706 unsafe {
2707 let publisher = abi::resolve_type_by_mangled_name(mangle::bound("7Combine5EmptyV", &[mangle::INT, mangle::NEVER]).as_bytes());
2708 assert!(!publisher.is_null(), "Empty<Int, Never> metadata");
2709 let wt = abi::get_view_wt_for_proto(publisher, resolve::publisher_proto());
2710 assert!(!wt.is_null(), "Empty<Int, Never>: Publisher");
2711 let value = vec![u8::from(complete_immediately); abi::type_bytes(publisher).max(1)];
2712 let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
2713 saw_published_int,
2714 boxed_ctx(Box::new(perform) as Box<dyn Fn(i64)>),
2715 ))) as *mut abi::ValueClosureCtx;
2716 generic1_modifier_ints(
2717 resolve::on_receive_fn(),
2718 resolve::on_receive_oret(),
2719 view,
2720 publisher,
2721 wt,
2722 &[
2723 value.as_ptr() as u64,
2724 abi::value_closure_thunk as *const () as u64,
2725 ctx as u64,
2726 ],
2727 [0.0; 4],
2728 )
2729 }
2730}
2731
2732#[doc(hidden)]
2734pub unsafe fn apply_modifier_for_test(
2735 view: &ViewHandle,
2736 modifier_meta: *const c_void,
2737 bytes: &[u8],
2738) -> ViewHandle {
2739 unsafe { apply_modifier(view, modifier_meta, bytes) }
2740}
2741
2742pub fn focused_value(view: &ViewHandle, present: bool) -> ViewHandle {
2750 unsafe {
2751 let meta = abi::resolve_type_by_mangled_name(
2752 mangle::Type::Nominal {
2753 module: "Foundation",
2754 path: vec!["JSONDecoder"],
2755 kind: mangle::Kind::Class,
2756 }
2757 .mangle()
2758 .as_bytes(),
2759 );
2760 assert!(!meta.is_null(), "Foundation.JSONDecoder metadata");
2761 let wt = crate::conformance::observable().witness_table();
2762 let object = if present {
2763 let o: u64;
2764 core::arch::asm!("blr {f}", f = in(reg) resolve::json_decoder_init(),
2765 inlateout("x0") meta => o, in("x20") meta,
2766 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2767 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
2768 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2769 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2770 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2771 );
2772 o
2773 } else {
2774 0
2776 };
2777 generic1_modifier_ints(
2778 resolve::focused_value_fn(),
2779 resolve::focused_value_oret(),
2780 view,
2781 meta,
2782 wt,
2783 &[object],
2784 [0.0; 4],
2785 )
2786 }
2787}
2788
2789static FRAME: std::sync::OnceLock<(Vec<u8>, usize)> = std::sync::OnceLock::new();
2795static TRACK: std::sync::OnceLock<(Vec<u8>, usize)> = std::sync::OnceLock::new();
2796
2797unsafe extern "C" fn emit_frame(out: *mut u8, _payload: *mut c_void) {
2799 let (bytes, meta) = FRAME.get().expect("keyframe built before the closure runs");
2800 unsafe { abi::copy_value(out, bytes.as_ptr(), *meta as *const c_void) };
2801}
2802
2803unsafe extern "C" fn emit_track(out: *mut u8, _payload: *mut c_void) {
2805 let (bytes, meta) = TRACK.get().expect("track built before the closure runs");
2806 unsafe { abi::copy_value(out, bytes.as_ptr(), *meta as *const c_void) };
2807}
2808
2809unsafe fn keyframe_track(to: f64) -> (*const c_void, *const c_void) {
2818 unsafe {
2819 let double = resolve::double_meta();
2820 let frame = abi::resolve_type_by_mangled_name(
2821 mangle::ty("MoveKeyframe")
2822 .of(vec![mangle::Type::Verbatim("Sd".into())])
2823 .mangle()
2824 .as_bytes(),
2825 );
2826 assert!(!frame.is_null(), "MoveKeyframe<Double> metadata");
2827 let frame_wt =
2828 abi::get_view_wt_for_proto(frame, resolve::keyframe_track_content_proto());
2829 assert!(!frame_wt.is_null(), "MoveKeyframe<Double>: KeyframeTrackContent");
2830
2831 let mut at = to;
2832 let mut one = vec![0u8; abi::type_bytes(frame)];
2833 abi::call_out(
2834 resolve::move_keyframe_init(),
2835 abi::ints(&[&mut at as *mut f64 as u64, double as u64]),
2836 [0.0; 4],
2837 frame as u64,
2838 one.as_mut_ptr(),
2839 );
2840 let _ = FRAME.set((one, frame as usize));
2841
2842 let track = abi::resolve_type_by_mangled_name(
2843 mangle::ty("KeyframeTrack")
2844 .of(vec![
2845 mangle::Type::Verbatim("Sd".into()),
2846 mangle::Type::Verbatim("Sd".into()),
2847 mangle::ty("MoveKeyframe").of(vec![mangle::Type::Verbatim("Sd".into())]),
2848 ])
2849 .mangle()
2850 .as_bytes(),
2851 );
2852 assert!(!track.is_null(), "KeyframeTrack metadata");
2853 let track_wt = abi::get_view_wt_for_proto(track, resolve::keyframes_proto());
2854 assert!(!track_wt.is_null(), "KeyframeTrack: Keyframes");
2855
2856 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(emit_frame, core::ptr::null_mut())))
2857 as *mut abi::ClosureCtx;
2858 let mut bytes = vec![0u8; abi::type_bytes(track)];
2859 abi::call_spilled(
2860 resolve::keyframe_track_init(),
2861 &[
2862 abi::swift_closure_thunk as *const () as u64,
2863 ctx as u64,
2864 double as u64,
2865 frame as u64,
2866 frame_wt as u64,
2867 ],
2868 [0.0; 4],
2869 track as u64,
2870 bytes.as_mut_ptr(),
2871 );
2872 let _ = TRACK.set((bytes, track as usize));
2873 (track, track_wt)
2874 }
2875}
2876
2877pub fn keyframe_animator(
2884 view: &ViewHandle,
2885 initial: f64,
2886 to: f64,
2887 repeating: bool,
2888 content: &ViewHandle,
2889) -> ViewHandle {
2890 unsafe {
2891 let (track, track_wt) = keyframe_track(to);
2892 let double = resolve::double_meta();
2893 let av = content.meta;
2897 let aw = content.wt;
2898 let subst = [view.meta, double, av, track, view.wt, aw, track_wt];
2899 let (meta, wt, size) = opaque_result_type(resolve::keyframe_animator_oret(), &subst);
2900
2901 let keyframes = Box::leak(Box::new(abi::ClosureCtx::new(
2902 emit_track,
2903 core::ptr::null_mut(),
2904 ))) as *mut abi::ClosureCtx;
2905
2906 let mut start = initial;
2907 let mut ints = vec![&mut start as *mut f64 as u64, u64::from(repeating)];
2908 ints.extend_from_slice(&view_words(content));
2909 ints.extend_from_slice(&[
2910 abi::swift_closure_thunk as *const () as u64,
2911 keyframes as u64,
2912 ]);
2913 ints.extend_from_slice(&[view.meta as u64, double as u64, av as u64, track as u64]);
2914 ints.extend_from_slice(&[view.wt as u64, aw as u64, track_wt as u64]);
2915 let r = abi::call_modifier_generic(resolve::keyframe_animator_fn(), view.value_ptr(), &ints, [0.0; 4], size);
2916 wrap_owned(r, meta, wt)
2917 }
2918}
2919
2920#[derive(Clone, Copy)]
2927pub struct EnvironmentValues(*const c_void);
2928
2929impl EnvironmentValues {
2930 pub unsafe fn bool_at(self, getter: *const c_void) -> bool {
2939 unsafe { abi::call_regs(getter, abi::ints(&[]), [0.0; 4], self.0 as u64)[0] & 1 != 0 }
2940 }
2941
2942 pub fn voice_over_enabled(self) -> bool {
2944 unsafe { self.bool_at(resolve::env_accessibility_voice_over()) }
2945 }
2946
2947 pub fn switch_control_enabled(self) -> bool {
2949 unsafe { self.bool_at(resolve::env_accessibility_switch_control()) }
2950 }
2951
2952 pub fn value_for<T: super::KeyValue>(self, key: &super::CustomEnvKey<T>) -> T {
2963 unsafe {
2964 static GET: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
2965 let f = *GET.get_or_init(|| {
2966 libc::dlsym((-2isize) as *mut c_void, c"swift_getAtKeyPath".as_ptr()) as usize
2967 });
2968 assert!(f != 0, "swift_getAtKeyPath");
2969 let mut out = vec![0u8; abi::type_bytes(T::metadata())];
2970 core::arch::asm!("blr {f}", f = in(reg) f,
2971 in("x0") self.0,
2972 in("x1") key.key_path_for_test(),
2973 in("x8") out.as_mut_ptr(),
2974 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
2975 lateout("x6") _, lateout("x7") _,
2976 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2977 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2978 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
2979 clobber_abi("C"),
2980 );
2981 let value = T::read(out.as_ptr());
2982 abi::type_destroy(T::metadata(), out.as_mut_ptr());
2983 value
2984 }
2985 }
2986}
2987
2988type EnvBuilder = Box<dyn Fn(EnvironmentValues) -> ViewHandle>;
2989
2990unsafe extern "C" fn emit_environment_view(out: *mut u8, env: i64, payload: *mut c_void) {
2993 unsafe {
2994 let build = &*(payload as *const EnvBuilder);
2995 let view = build(EnvironmentValues(env as *const c_void));
2996 (out as *mut u64).write(leak_retained(&view));
2997 }
2998}
2999
3000pub fn environment_reader(
3018 builder: impl Fn(EnvironmentValues) -> ViewHandle + 'static,
3019) -> ViewHandle {
3020 unsafe {
3021 let boxed: EnvBuilder = Box::new(builder);
3022 let payload = Box::into_raw(Box::new(boxed)) as *mut c_void;
3023 let ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(emit_environment_view, payload)))
3024 as *mut abi::IndexClosureCtx;
3025
3026 let ty = mangle::ty("EnvironmentReader").of(vec![mangle::ty("AnyView")]).mangle();
3027 let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
3028 assert!(!meta.is_null(), "EnvironmentReader<AnyView> metadata");
3029
3030 let mut value = vec![0u8; abi::type_bytes(meta)];
3035 abi::call_out(
3036 resolve::environment_reader_init(),
3037 abi::ints(&[abi::index_closure_thunk as *const c_void as u64, ctx as u64]),
3038 [0.0; 4],
3039 meta as u64,
3040 value.as_mut_ptr().cast(),
3041 );
3042 let wt = abi::get_view_wt(meta);
3043 assert!(!wt.is_null(), "EnvironmentReader !: View");
3044 ViewHandle::wrap(value.as_ptr() as *const c_void, meta, wt)
3045 }
3046}
3047
3048#[cfg(test)]
3049mod geometry_tests {
3050 use super::*;
3051
3052 #[test]
3060 fn scroll_geometry_reader_matches_the_live_layout() {
3061 let &[offset, size, insets, container, visible] =
3062 scroll_geometry_offsets().expect("five stored fields");
3063 let bytes = unsafe { abi::type_bytes(abi::metadata0(resolve::scroll_geometry_ma())) };
3064 let mut buf = vec![0u8; bytes];
3065 let mut put = |at: usize, v: f64| buf[at..at + 8].copy_from_slice(&v.to_le_bytes());
3066 for (base, n) in [(offset, 2), (size, 2), (insets, 4), (container, 2), (visible, 4)] {
3067 for i in 0..n {
3068 put(base + i * 8, (base + i * 8) as f64);
3069 }
3070 }
3071
3072 let g = unsafe { read_scroll_geometry(buf.as_ptr()) };
3073 let f = |o: usize| o as f64;
3074 assert_eq!(g.content_offset, (f(offset), f(offset + 8)));
3075 assert_eq!(g.content_size, (f(size), f(size + 8)));
3076 assert_eq!(
3077 g.content_insets,
3078 (f(insets), f(insets + 8), f(insets + 16), f(insets + 24)),
3079 "top, leading, bottom, trailing — in that order"
3080 );
3081 assert_eq!(g.container_size, (f(container), f(container + 8)));
3082 assert_eq!(
3083 g.visible_rect,
3084 (f(visible), f(visible + 8), f(visible + 16), f(visible + 24)),
3085 "CGRect is origin then size, so x, y, width, height"
3086 );
3087 }
3088}