1use super::*;
4use crate::abi;
5use crate::resolve;
6use core::ffi::c_void;
7use crate::mangle::swift_type;
8pub(crate) fn action_words(action: impl Fn() + 'static) -> [u64; 2] {
23 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(invoke_action, boxed_ctx(Box::new(action) as Box<dyn Fn()>))))
24 as *mut abi::ClosureCtx;
25 [abi::swift_closure_thunk as *const () as u64, ctx as u64]
26}
27
28pub(crate) fn boxed_action_words(action: Box<dyn Fn()>) -> [u64; 2] {
33 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
34 super::controls::run_boxed_action,
35 super::boxed_ctx(action),
36 ))) as *mut abi::ClosureCtx;
37 [abi::swift_closure_thunk as *const () as u64, ctx as u64]
38}
39
40pub(crate) fn view_words(view: &ViewHandle) -> [u64; 2] {
47 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
48 emit_tuple,
49 Child::leak(view, Content::Concrete),
50 ))) as *mut abi::ClosureCtx;
51 [abi::swift_closure_thunk as *const () as u64, ctx as u64]
52}
53
54pub(crate) fn wide_words(
56 body: unsafe extern "C" fn(u64, u64, u64, *mut c_void),
57 payload: *mut c_void,
58) -> [u64; 2] {
59 let ctx = Box::leak(Box::new(abi::WideClosureCtx::new(body, payload)))
60 as *mut abi::WideClosureCtx;
61 [abi::wide_closure_thunk as *const () as u64, ctx as u64]
62}
63
64pub(crate) fn pair_words(
66 body: unsafe extern "C" fn(*const u8, *const u8, *mut c_void),
67 payload: *mut c_void,
68) -> [u64; 2] {
69 let ctx = Box::leak(Box::new(abi::PairClosureCtx::new(body, payload)))
70 as *mut abi::PairClosureCtx;
71 [abi::pair_closure_thunk as *const () as u64, ctx as u64]
72}
73
74pub(crate) struct TagCallback {
81 pub(crate) f: Box<dyn Fn(i64)>,
82 pub(crate) meta: *const c_void,
83 pub(crate) indirect: bool,
84}
85
86pub(crate) unsafe extern "C" fn invoke_tag(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
87 unsafe {
88 let cb = &*(payload as *const TagCallback);
89 let tag = if cb.indirect {
90 abi::read_enum_tag(a0 as *const u8, cb.meta)
91 } else {
92 abi::read_enum_tag(a0.to_le_bytes().as_ptr(), cb.meta)
93 };
94 (cb.f)(i64::from(tag));
95 }
96}
97
98pub(crate) unsafe fn tag_words(f: impl Fn(i64) + 'static, meta: *const c_void, indirect: bool) -> [u64; 2] {
100 let cb = Box::leak(Box::new(TagCallback { f: Box::new(f), meta, indirect }));
101 wide_words(invoke_tag, cb as *mut TagCallback as *mut c_void)
102}
103
104pub(crate) unsafe extern "C" fn invoke_bool(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
105 let f = unsafe { boxed_ref::<dyn Fn(bool)>(payload) };
106 f(a0 & 1 != 0);
107}
108
109pub(crate) fn bool_words(f: impl Fn(bool) + 'static) -> [u64; 2] {
111 wide_words(invoke_bool, boxed_ctx(Box::new(f) as Box<dyn Fn(bool)>))
112}
113
114pub(crate) unsafe extern "C" fn invoke_url(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
115 unsafe {
116 let f = boxed_ref::<dyn Fn(&str)>(payload);
117 let (s0, s1): (u64, u64);
120 core::arch::asm!("blr {f}", f = in(reg) resolve::url_absolute_string(),
121 in("x20") a0,
122 lateout("x0") s0, lateout("x1") s1,
123 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
124 lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
125 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
126 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
127 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
128 );
129 let mut raw = [0u8; 16];
130 raw[..8].copy_from_slice(&s0.to_le_bytes());
131 raw[8..].copy_from_slice(&s1.to_le_bytes());
132 f(&abi::read_swift_string(raw));
133 }
134}
135
136pub(crate) fn url_words(f: impl Fn(&str) + 'static) -> [u64; 2] {
138 wide_words(invoke_url, boxed_ctx(Box::new(f) as Box<dyn Fn(&str)>))
139}
140
141pub(crate) struct TagPairCallback {
143 pub(crate) f: Box<dyn Fn(i64, i64)>,
144 pub(crate) meta: *const c_void,
145}
146
147pub(crate) unsafe extern "C" fn invoke_tag_pair(a: *const u8, b: *const u8, payload: *mut c_void) {
148 unsafe {
149 let cb = &*(payload as *const TagPairCallback);
150 let ta = abi::read_enum_tag((a as u64).to_le_bytes().as_ptr(), cb.meta);
152 let tb = abi::read_enum_tag((b as u64).to_le_bytes().as_ptr(), cb.meta);
153 (cb.f)(i64::from(ta), i64::from(tb));
154 }
155}
156
157pub(crate) unsafe extern "C" fn invoke_word_pair(a: *const u8, b: *const u8, payload: *mut c_void) {
158 let f = unsafe { boxed_ref::<dyn Fn(u64, u64)>(payload) };
159 f(a as u64, b as u64);
160}
161
162pub(crate) unsafe fn generic1_modifier_ints(
170 func: *const c_void,
171 oret: *const c_void,
172 view: &ViewHandle,
173 arg_meta: *const c_void,
174 arg_wt: *const c_void,
175 leading: &[u64],
176 floats: [f64; 4],
177) -> ViewHandle {
178 unsafe {
179 let subst = [view.meta, arg_meta, view.wt, arg_wt];
180 let (meta, wt, size) = opaque_result_type(oret, &subst);
181 let mut ints = Vec::with_capacity(leading.len() + 4);
182 ints.extend_from_slice(leading);
183 ints.push(view.meta as u64);
184 ints.push(arg_meta as u64);
185 ints.push(view.wt as u64);
186 ints.push(arg_wt as u64);
187 let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, floats, size);
188 wrap_owned(r, meta, wt)
189 }
190}
191
192pub(crate) unsafe fn content_modifier(
194 func: *const c_void,
195 oret: *const c_void,
196 view: &ViewHandle,
197 leading: &[u64],
198 content: &ViewHandle,
199) -> ViewHandle {
200 unsafe {
201 let mut ints = leading.to_vec();
202 ints.extend_from_slice(&view_words(content));
203 generic1_modifier_ints(
204 func,
205 oret,
206 view,
207 content.meta,
208 content.wt,
209 &ints,
210 [0.0; 4],
211 )
212 }
213}
214
215pub(crate) unsafe fn frozen_static(meta: *const c_void, getter: &'static core::ffi::CStr) -> u64 {
221 unsafe {
222 let f = resolve::sym(getter);
223 assert!(!f.is_null(), "missing static: {}", crate::diag::symbol_name(getter));
224 abi::static_regs(f, meta)[0]
225 }
226}
227
228#[derive(Clone, Copy, Debug, PartialEq, Eq)]
235pub enum ActionKind {
236 Default,
238 Escape,
240 #[cfg(not(target_os = "macos"))]
244 MagicTap,
245 ShowMenu,
247 Delete,
249}
250
251impl ActionKind {
252 pub(crate) fn getter(self) -> &'static core::ffi::CStr {
253 match self {
254 Self::Default => c"$s7SwiftUI23AccessibilityActionKindV7defaultACvgZ",
255 Self::Escape => c"$s7SwiftUI23AccessibilityActionKindV6escapeACvgZ",
256 #[cfg(not(target_os = "macos"))]
257 Self::MagicTap => c"$s7SwiftUI23AccessibilityActionKindV8magicTapACvgZ",
258 Self::ShowMenu => c"$s7SwiftUI23AccessibilityActionKindV8showMenuACvgZ",
259 Self::Delete => c"$s7SwiftUI23AccessibilityActionKindV6deleteACvgZ",
260 }
261 }
262}
263
264swift_statics! {
265 pub enum SubmitTrigger {
267 Text = c"$s7SwiftUI14SubmitTriggersV4textACvgZ",
268 Search = c"$s7SwiftUI14SubmitTriggersV6searchACvgZ",
269 Collection = c"$s7SwiftUI14SubmitTriggersV10collectionACvgZ",
270 }
271}
272
273swift_statics! {
274 pub enum EventModifier {
276 Shift = c"$s7SwiftUI14EventModifiersV5shiftACvgZ",
277 Control = c"$s7SwiftUI14EventModifiersV7controlACvgZ",
278 Option = c"$s7SwiftUI14EventModifiersV6optionACvgZ",
279 Command = c"$s7SwiftUI14EventModifiersV7commandACvgZ",
280 CapsLock = c"$s7SwiftUI14EventModifiersV8capsLockACvgZ",
281 Function = c"$s7SwiftUI14EventModifiersV8functionACvgZ",
282 NumericPad = c"$s7SwiftUI14EventModifiersV10numericPadACvgZ",
283 All = c"$s7SwiftUI14EventModifiersV3allACvgZ",
284 }
285}
286
287pub fn accessibility_action(view: &ViewHandle, kind: ActionKind, action: impl Fn() + 'static) -> ViewHandle {
289 unsafe {
290 let meta = abi::metadata0(resolve::a11y_action_kind_ma());
291 let k = resilient_static(meta, kind.getter());
292 let c = action_words(action);
293 a11y_modifier(resolve::a11y_action_fn(), view, &[k.as_ptr() as u64, c[0], c[1]], [0.0; 4])
294 }
295}
296
297pub fn accessibility_adjustable_action(view: &ViewHandle, action: impl Fn(i64) + 'static) -> ViewHandle {
302 unsafe {
303 let meta = abi::metadata0(resolve::a11y_adjustment_direction_ma());
304 let c = tag_words(action, meta, true);
306 a11y_modifier(resolve::a11y_adjustable_action_fn(), view, &c, [0.0; 4])
307 }
308}
309
310pub fn accessibility_scroll_action(view: &ViewHandle, action: impl Fn(i64) + 'static) -> ViewHandle {
315 unsafe {
316 let meta = abi::metadata0(resolve::edge_enum_ma());
317 let c = tag_words(action, meta, false);
319 a11y_modifier(resolve::a11y_scroll_action_fn(), view, &c, [0.0; 4])
320 }
321}
322
323pub fn rename_action(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
325 unsafe {
326 opaque_modifier(
327 resolve::rename_action_fn(),
328 resolve::rename_action_oret(),
329 view,
330 &action_words(action),
331 [0.0; 4],
332 )
333 }
334}
335
336pub fn on_submit(view: &ViewHandle, trigger: SubmitTrigger, action: impl Fn() + 'static) -> ViewHandle {
338 unsafe {
339 let meta = abi::metadata0(resolve::submit_triggers_ma());
340 let t = resilient_static(meta, trigger.getter());
341 let c = action_words(action);
342 opaque_modifier(
343 resolve::on_submit_fn(),
344 resolve::on_submit_oret(),
345 view,
346 &[t.as_ptr() as u64, c[0], c[1]],
347 [0.0; 4],
348 )
349 }
350}
351
352pub fn on_delete_command(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
354 unsafe {
355 opaque_modifier(
356 resolve::on_delete_command_fn(),
357 resolve::on_delete_command_oret(),
358 view,
359 &action_words(action),
360 [0.0; 4],
361 )
362 }
363}
364
365pub fn on_exit_command(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
367 unsafe {
368 opaque_modifier(
369 resolve::on_exit_command_fn(),
370 resolve::on_exit_command_oret(),
371 view,
372 &action_words(action),
373 [0.0; 4],
374 )
375 }
376}
377
378pub fn on_move_command(view: &ViewHandle, action: impl Fn(i64) + 'static) -> ViewHandle {
382 unsafe {
383 let meta = abi::metadata0(resolve::move_command_direction_ma());
384 let c = tag_words(action, meta, true);
385 opaque_modifier(
386 resolve::on_move_command_fn(),
387 resolve::on_move_command_oret(),
388 view,
389 &c,
390 [0.0; 4],
391 )
392 }
393}
394
395pub fn on_interactive_resize_change(view: &ViewHandle, action: impl Fn(bool) + 'static) -> ViewHandle {
397 unsafe {
398 opaque_modifier(
399 resolve::on_interactive_resize_fn(),
400 resolve::on_interactive_resize_oret(),
401 view,
402 &bool_words(action),
403 [0.0; 4],
404 )
405 }
406}
407
408pub fn on_scroll_visibility_change(
410 view: &ViewHandle,
411 threshold: f64,
412 action: impl Fn(bool) + 'static,
413) -> ViewHandle {
414 unsafe {
415 opaque_modifier(
416 resolve::on_scroll_visibility_fn(),
417 resolve::on_scroll_visibility_oret(),
418 view,
419 &bool_words(action),
420 [threshold, 0.0, 0.0, 0.0],
421 )
422 }
423}
424
425pub fn on_open_url(view: &ViewHandle, action: impl Fn(&str) + 'static) -> ViewHandle {
427 unsafe {
428 opaque_modifier(
429 resolve::on_open_url_fn(),
430 resolve::on_open_url_oret(),
431 view,
432 &url_words(action),
433 [0.0; 4],
434 )
435 }
436}
437
438pub fn on_scroll_phase_change(view: &ViewHandle, action: impl Fn(i64, i64) + 'static) -> ViewHandle {
443 unsafe {
444 let meta = abi::metadata0(resolve::scroll_phase_ma());
445 let cb = Box::leak(Box::new(TagPairCallback { f: Box::new(action), meta }));
446 let c = pair_words(invoke_tag_pair, cb as *mut TagPairCallback as *mut c_void);
447 opaque_modifier(
448 resolve::on_scroll_phase_change_fn(),
449 resolve::on_scroll_phase_change_oret(),
450 view,
451 &c,
452 [0.0; 4],
453 )
454 }
455}
456
457#[derive(Clone, Copy, Debug)]
463pub struct TransactionRef(*mut c_void);
464
465impl TransactionRef {
466 pub fn set_disables_animations(self, value: bool) {
468 unsafe {
469 abi::call_out(
470 resolve::transaction_disables_animations_set(),
471 abi::ints(&[u64::from(value)]),
472 [0.0; 4],
473 self.0 as u64,
474 core::ptr::null_mut(),
475 );
476 }
477 }
478}
479
480pub(crate) unsafe extern "C" fn invoke_transaction(a0: u64, _a1: u64, _a2: u64, payload: *mut c_void) {
481 let f = unsafe { boxed_ref::<dyn Fn(TransactionRef)>(payload) };
482 f(TransactionRef(a0 as *mut c_void));
483}
484
485pub fn transaction(view: &ViewHandle, action: impl Fn(TransactionRef) + 'static) -> ViewHandle {
490 unsafe {
491 let c = wide_words(invoke_transaction, boxed_ctx(Box::new(action) as Box<dyn Fn(TransactionRef)>));
492 opaque_modifier(resolve::transaction_fn(), resolve::transaction_oret(), view, &c, [0.0; 4])
493 }
494}
495
496pub fn accessibility_children(view: &ViewHandle, children: &ViewHandle) -> ViewHandle {
498 unsafe {
499 content_modifier(
500 resolve::a11y_children_fn(),
501 resolve::a11y_children_oret(),
502 view,
503 &[],
504 children,
505 )
506 }
507}
508
509pub fn accessibility_representation(view: &ViewHandle, representation: &ViewHandle) -> ViewHandle {
511 unsafe {
512 content_modifier(
513 resolve::a11y_representation_fn(),
514 resolve::a11y_representation_oret(),
515 view,
516 &[],
517 representation,
518 )
519 }
520}
521
522swift_statics! {
523 pub enum ActionCategory {
525 Default = c"$s7SwiftUI27AccessibilityActionCategoryV7defaultACvgZ",
526 Edit = c"$s7SwiftUI27AccessibilityActionCategoryV4editACvgZ",
527 }
528}
529
530pub fn accessibility_actions(
532 view: &ViewHandle,
533 category: ActionCategory,
534 content: &ViewHandle,
535) -> ViewHandle {
536 unsafe {
537 let meta = abi::metadata0(resolve::a11y_action_category_ma());
538 let c = resilient_static(meta, category.getter());
539 content_modifier(
540 resolve::a11y_actions_fn(),
541 resolve::a11y_actions_oret(),
542 view,
543 &[c.as_ptr() as u64],
544 content,
545 )
546 }
547}
548
549pub fn toolbar_title_menu(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
551 unsafe {
552 content_modifier(
553 resolve::toolbar_title_menu_fn(),
554 resolve::toolbar_title_menu_oret(),
555 view,
556 &[],
557 content,
558 )
559 }
560}
561
562pub fn section_actions(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
564 unsafe {
565 content_modifier(
566 resolve::section_actions_fn(),
567 resolve::section_actions_oret(),
568 view,
569 &[],
570 content,
571 )
572 }
573}
574
575pub fn tab_item(view: &ViewHandle, label: &ViewHandle) -> ViewHandle {
577 unsafe {
578 content_modifier(resolve::tab_item_fn(), resolve::tab_item_oret(), view, &[], label)
579 }
580}
581
582#[cfg(test)]
583mod closure_tests {
584 use super::*;
585 use std::sync::atomic::{AtomicI64, Ordering};
586
587 unsafe fn call_closure(words: [u64; 2], arg: u64) {
594 unsafe {
595 core::arch::asm!("blr {f}", f = in(reg) words[0],
596 in("x0") arg, in("x20") words[1],
597 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
598 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
599 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
600 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
601 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
602 );
603 }
604 }
605
606 static SEEN: AtomicI64 = AtomicI64::new(-1);
607
608 fn record(v: i64) {
609 SEEN.store(v, Ordering::SeqCst);
610 }
611
612 #[test]
613 fn frozen_enum_case_arrives_in_the_register() {
614 unsafe {
615 let meta = abi::metadata0(resolve::edge_enum_ma());
617 let words = tag_words(record, meta, false);
618 for case in 0..4u64 {
619 SEEN.store(-1, Ordering::SeqCst);
620 call_closure(words, case);
621 assert_eq!(SEEN.load(Ordering::SeqCst), case as i64, "Edge case {case}");
622 }
623 }
624 }
625
626 #[test]
627 fn resilient_enum_case_arrives_by_address() {
628 unsafe {
629 let meta = abi::metadata0(resolve::move_command_direction_ma());
632 let words = tag_words(record, meta, true);
633 for case in 0..4u32 {
634 let mut buf = vec![0u8; abi::type_bytes(meta)];
635 abi::inject_enum_tag(&mut buf, meta, case);
636 SEEN.store(-1, Ordering::SeqCst);
637 call_closure(words, buf.as_ptr() as u64);
638 assert_eq!(SEEN.load(Ordering::SeqCst), i64::from(case), "direction {case}");
639 }
640 }
641 }
642
643 fn record_bool(v: bool) {
644 SEEN.store(i64::from(v), Ordering::SeqCst);
645 }
646
647 #[test]
648 fn bool_closure_reads_the_low_bit() {
649 let words = bool_words(record_bool);
650 unsafe {
651 SEEN.store(-1, Ordering::SeqCst);
652 call_closure(words, 1);
653 assert_eq!(SEEN.load(Ordering::SeqCst), 1);
654 call_closure(words, 0);
655 assert_eq!(SEEN.load(Ordering::SeqCst), 0);
656 }
657 }
658
659 #[test]
661 fn enum_tag_round_trips() {
662 unsafe {
663 let meta = abi::metadata0(resolve::scroll_phase_ma());
664 let mut buf = vec![0u8; abi::type_bytes(meta)];
665 for case in 0..5u32 {
666 abi::inject_enum_tag(&mut buf, meta, case);
667 assert_eq!(abi::read_enum_tag(buf.as_ptr(), meta), case);
668 }
669 }
670 }
671
672 #[test]
676 fn closure_context_outlives_construction() {
677 let words = {
678 let view = text("gone by now");
679 view_words(&view)
680 };
681 assert_ne!(words[0], 0);
682 assert_ne!(words[1], 0);
683 let mut out = 0u64;
684 unsafe {
685 core::arch::asm!("blr {f}", f = in(reg) words[0],
686 in("x8") &mut out as *mut u64, in("x20") words[1],
687 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
688 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
689 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
690 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
691 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
692 );
693 }
694 assert_ne!(out, 0, "the ViewBuilder closure handed back nothing");
695 }
696}
697
698pub(crate) fn ignoring_value_words(action: impl Fn() + 'static) -> [u64; 2] {
705 wide_words(invoke_ignoring_value, boxed_ctx(Box::new(action) as Box<dyn Fn()>))
706}
707
708pub(crate) unsafe extern "C" fn invoke_ignoring_value(
709 _a0: u64,
710 _a1: u64,
711 _a2: u64,
712 payload: *mut c_void,
713) {
714 let f = unsafe { boxed_ref::<dyn Fn()>(payload) };
715 f();
716}
717
718pub fn accessibility_zoom_action(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
720 unsafe { a11y_modifier(resolve::a11y_zoom_action_fn(), view, &ignoring_value_words(action), [0.0; 4]) }
721}
722
723pub fn on_continue_user_activity(view: &ViewHandle, kind: &str, action: impl Fn() + 'static) -> ViewHandle {
725 unsafe {
726 let s = abi::swift_string(kind);
727 let c = ignoring_value_words(action);
728 opaque_modifier(
729 resolve::on_continue_user_activity_fn(),
730 resolve::on_continue_user_activity_oret(),
731 view,
732 &[
733 u64::from_le_bytes(s[..8].try_into().expect("lo")),
734 u64::from_le_bytes(s[8..].try_into().expect("hi")),
735 c[0],
736 c[1],
737 ],
738 [0.0; 4],
739 )
740 }
741}
742
743pub fn presentation_background(
745 view: &ViewHandle,
746 alignment: Alignment,
747 content: &ViewHandle,
748) -> ViewHandle {
749 unsafe {
750 let a = alignment.words();
751 content_modifier(
752 resolve::presentation_background_fn(),
753 resolve::presentation_background_oret(),
754 view,
755 &[a[0], a[1]],
756 content,
757 )
758 }
759}
760
761swift_statics! {
762 pub enum ContentType {
764 PlainText = c"$s22UniformTypeIdentifiers6UTTypeV9plainTextACvgZ",
765 Text = c"$s22UniformTypeIdentifiers6UTTypeV4textACvgZ",
766 Url = c"$s22UniformTypeIdentifiers6UTTypeV3urlACvgZ",
767 }
768}
769
770pub(crate) fn word_closure(
772 body: unsafe extern "C" fn(*mut c_void, u64) -> u64,
773 payload: *mut c_void,
774) -> [u64; 2] {
775 let ctx = Box::leak(Box::new(abi::WordClosureCtx::new(body, payload)))
776 as *mut abi::WordClosureCtx;
777 [abi::word_closure_thunk as *const () as u64, ctx as u64]
778}
779
780unsafe impl Send for ItemProvider {}
781unsafe impl Sync for ItemProvider {}
782
783pub(crate) unsafe fn provider_array(items: &[&str]) -> u64 {
785 unsafe {
786 let meta = crate::type_by_name!(swift_type!(objc NSItemProvider).as_bytes());
787 let stride = abi::type_bytes(meta);
788 let mut bytes = vec![0u8; stride * items.len()];
789 for (i, t) in items.iter().enumerate() {
790 let p = ItemProvider::plain_text(t);
791 bytes[i * stride..i * stride + 8].copy_from_slice(&(p.0 as u64).to_le_bytes());
792 }
793 abi::swift_array(meta, items.len(), &bytes)
794 }
795}
796
797pub(crate) type DropDecide = Box<dyn Fn(&[ItemProvider]) -> bool>;
799
800pub(crate) type DropObserve = Box<dyn Fn(&[ItemProvider])>;
802
803pub(crate) struct ProviderPayload {
805 array: u64,
806 decide: Option<DropDecide>,
807 observe: Option<DropObserve>,
808}
809
810pub(crate) unsafe extern "C" fn emit_providers(payload: *mut c_void, _arg: u64) -> u64 {
811 unsafe {
812 let p = &*(payload as *const ProviderPayload);
813 swift_runtime_sys::RuntimeRaw::swift_retain(p.array as *mut c_void);
814 p.array
815 }
816}
817
818pub(crate) unsafe extern "C" fn emit_first_provider(payload: *mut c_void, _arg: u64) -> u64 {
819 unsafe {
820 let p = &*(payload as *const ProviderPayload);
821 let items = read_provider_array(p.array);
822 items.first().map_or(0, |i| i.0 as u64)
823 }
824}
825
826pub(crate) unsafe extern "C" fn decide_on_providers(payload: *mut c_void, arg: u64) -> u64 {
827 unsafe {
828 let p = &*(payload as *const ProviderPayload);
829 let items = read_provider_array(arg);
830 u64::from(p.decide.as_ref().map(|f| f(&items)).unwrap_or(false))
831 }
832}
833
834pub(crate) unsafe extern "C" fn observe_providers(
835 a0: u64,
836 _a1: u64,
837 _a2: u64,
838 payload: *mut c_void,
839) {
840 unsafe {
841 let p = &*(payload as *const ProviderPayload);
842 if let Some(f) = p.observe.as_ref() {
843 f(&read_provider_array(a0));
844 }
845 }
846}
847
848pub(crate) fn provider_payload(
849 array: u64,
850 decide: Option<DropDecide>,
851 observe: Option<DropObserve>,
852) -> *mut c_void {
853 Box::leak(Box::new(ProviderPayload { array, decide, observe })) as *mut ProviderPayload
854 as *mut c_void
855}
856
857swift_statics! {
858 pub enum ContainerBackgroundPlacement {
867 Window = c"$s7SwiftUI28ContainerBackgroundPlacementV6windowACvgZ",
868 #[cfg(not(target_os = "macos"))]
871 Navigation = c"$s7SwiftUI28ContainerBackgroundPlacementV10navigationACvgZ",
872 Presentation = c"$s7SwiftUI28ContainerBackgroundPlacementV12presentationACvgZ",
873 #[cfg(not(target_os = "macos"))]
876 NavigationSplitView = c"$s7SwiftUI28ContainerBackgroundPlacementV19navigationSplitViewACvgZ",
877 }
878}
879
880swift_cases! {
881 pub enum VerticalEdge {
883 Top = 0,
884 Bottom = 1,
885 }
886}
887
888pub fn container_background(
890 view: &ViewHandle,
891 placement: ContainerBackgroundPlacement,
892 alignment: Alignment,
893 content: &ViewHandle,
894) -> ViewHandle {
895 unsafe {
896 let meta = abi::metadata0(resolve::container_background_placement_ma());
897 let p = resilient_static(meta, placement.getter());
898 let a = alignment.words();
899 content_modifier(
900 resolve::container_background_fn(),
901 resolve::container_background_oret(),
902 view,
903 &[p.as_ptr() as u64, a[0], a[1]],
904 content,
905 )
906 }
907}
908
909unsafe fn safe_area_args(
911 edge: VerticalEdge,
912 alignment: HorizontalAlignment,
913 spacing: Option<f64>,
914) -> Vec<u64> {
915 unsafe {
916 let e = abi::enum_argument(abi::metadata0(resolve::vertical_edge_ma()), edge.tag());
917 let a = frozen_static(abi::metadata0(resolve::horizontal_alignment_ma()), alignment.getter());
918 let mut ints = vec![e, a];
919 ints.extend(opt_f64_words(spacing));
920 ints
921 }
922}
923
924pub fn safe_area_inset(
926 view: &ViewHandle,
927 edge: VerticalEdge,
928 alignment: HorizontalAlignment,
929 spacing: Option<f64>,
930 content: &ViewHandle,
931) -> ViewHandle {
932 unsafe {
933 let args = safe_area_args(edge, alignment, spacing);
934 content_modifier(
935 resolve::safe_area_inset_fn(),
936 resolve::safe_area_inset_oret(),
937 view,
938 &args,
939 content,
940 )
941 }
942}
943
944pub fn safe_area_bar(
946 view: &ViewHandle,
947 edge: VerticalEdge,
948 alignment: HorizontalAlignment,
949 spacing: Option<f64>,
950 content: &ViewHandle,
951) -> ViewHandle {
952 unsafe {
953 let args = safe_area_args(edge, alignment, spacing);
954 content_modifier(
955 resolve::safe_area_bar_fn(),
956 resolve::safe_area_bar_oret(),
957 view,
958 &args,
959 content,
960 )
961 }
962}
963
964pub fn user_activity(view: &ViewHandle, kind: &str, active: bool, action: impl Fn() + 'static) -> ViewHandle {
966 unsafe {
967 let s = abi::swift_string(kind);
968 let c = ignoring_value_words(action);
969 opaque_modifier(
970 resolve::user_activity_fn(),
971 resolve::user_activity_oret(),
972 view,
973 &[
974 u64::from_le_bytes(s[..8].try_into().expect("lo")),
975 u64::from_le_bytes(s[8..].try_into().expect("hi")),
976 u64::from(active),
977 c[0],
978 c[1],
979 ],
980 [0.0; 4],
981 )
982 }
983}
984
985swift_statics! {
986 pub enum SensoryFeedback {
988 Success = c"$s7SwiftUI15SensoryFeedbackV7successACvgZ",
989 Warning = c"$s7SwiftUI15SensoryFeedbackV7warningACvgZ",
990 Error = c"$s7SwiftUI15SensoryFeedbackV5errorACvgZ",
991 Selection = c"$s7SwiftUI15SensoryFeedbackV9selectionACvgZ",
992 Impact = c"$s7SwiftUI15SensoryFeedbackV6impactACvgZ",
993 Alignment = c"$s7SwiftUI15SensoryFeedbackV9alignmentACvgZ",
994 Decrease = c"$s7SwiftUI15SensoryFeedbackV8decreaseACvgZ",
995 Increase = c"$s7SwiftUI15SensoryFeedbackV8increaseACvgZ",
996 LevelChange = c"$s7SwiftUI15SensoryFeedbackV11levelChangeACvgZ",
997 PathComplete = c"$s7SwiftUI15SensoryFeedbackV12pathCompleteACvgZ",
998 Start = c"$s7SwiftUI15SensoryFeedbackV5startACvgZ",
999 Stop = c"$s7SwiftUI15SensoryFeedbackV4stopACvgZ",
1000 }
1001}
1002
1003pub(crate) fn words(bytes: &[u8]) -> Vec<u64> {
1005 bytes
1006 .chunks(8)
1007 .map(|c| {
1008 let mut w = [0u8; 8];
1009 w[..c.len()].copy_from_slice(c);
1010 u64::from_le_bytes(w)
1011 })
1012 .collect()
1013}
1014
1015swift_statics! {
1016 pub enum SearchFieldPlacement {
1021 Automatic = c"$s7SwiftUI20SearchFieldPlacementV9automaticACvgZ",
1022 ToolbarPrincipal = c"$s7SwiftUI20SearchFieldPlacementV16toolbarPrincipalACvgZ",
1023 NavigationColumn = c"$s7SwiftUI20SearchFieldPlacementV16navigationColumnACvgZ",
1024 #[cfg(not(target_os = "macos"))]
1027 NavigationBarDrawer = c"$s7SwiftUI20SearchFieldPlacementV19navigationBarDrawerACvgZ",
1028 Sidebar = c"$s7SwiftUI20SearchFieldPlacementV7sidebarACvgZ",
1029 Toolbar = c"$s7SwiftUI20SearchFieldPlacementV7toolbarACvgZ",
1030 }
1031}
1032
1033pub(crate) unsafe fn two_view_modifier(
1042 func: *const c_void,
1043 oret: *const c_void,
1044 view: &ViewHandle,
1045 leading: &[u64],
1046 a: &ViewHandle,
1047 b: &ViewHandle,
1048) -> ViewHandle {
1049 unsafe {
1050 let subst = [view.meta, a.meta, b.meta, view.wt, a.wt, b.wt];
1055 let (meta, wt, size) = opaque_result_type(oret, &subst);
1056 let mut ints = leading.to_vec();
1057 ints.extend_from_slice(&view_words(a));
1058 ints.extend_from_slice(&view_words(b));
1059 ints.extend_from_slice(&[view.meta as u64, a.meta as u64, b.meta as u64]);
1060 ints.extend_from_slice(&[view.wt as u64, a.wt as u64, b.wt as u64]);
1061 let r = abi::call_modifier_generic(func, view.value_ptr(), &ints, [0.0; 4], size);
1062 wrap_owned(r, meta, wt)
1063 }
1064}
1065
1066swift_statics! {
1067 pub enum SearchSuggestionsPlacementSet {
1069 Menu = c"$s7SwiftUI26SearchSuggestionsPlacementV3SetV4menuAEvgZ",
1070 Content = c"$s7SwiftUI26SearchSuggestionsPlacementV3SetV7contentAEvgZ",
1071 }
1072}
1073
1074swift_statics! {
1075 pub enum TextInputFormattingControlPlacementSet {
1077 All = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV3allAEvgZ",
1078 Default = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV7defaultAEvgZ",
1079 ContextMenu = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV11contextMenuAEvgZ",
1080 AccessoryBar = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV12accessoryBarAEvgZ",
1081 #[cfg(not(target_os = "macos"))]
1084 InputAssistant = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV14inputAssistantAEvgZ",
1085 FontPanel = c"$s7SwiftUI35TextInputFormattingControlPlacementV3SetV9fontPanelAEvgZ",
1086 }
1087}
1088
1089swift_statics! {
1090 pub enum CharacterSetKind {
1099 Alphanumerics = c"$s10Foundation12CharacterSetV13alphanumericsACvgZ",
1100 Letters = c"$s10Foundation12CharacterSetV7lettersACvgZ",
1101 Decimals = c"$s10Foundation12CharacterSetV13decimalDigitsACvgZ",
1102 Whitespaces = c"$s10Foundation12CharacterSetV11whitespacesACvgZ",
1103 }
1104}
1105
1106swift_statics! {
1107 pub enum KeyPressPhases {
1109 Down = c"$s7SwiftUI8KeyPressV6PhasesV4downAEvgZ",
1110 Up = c"$s7SwiftUI8KeyPressV6PhasesV2upAEvgZ",
1111 Repeat = c"$s7SwiftUI8KeyPressV6PhasesV6repeatAEvgZ",
1112 All = c"$s7SwiftUI8KeyPressV6PhasesV3allAEvgZ",
1113 }
1114}
1115
1116pub fn navigation_transition(view: &ViewHandle) -> ViewHandle {
1123 unsafe {
1124 let meta = abi::metadata0(resolve::automatic_navigation_transition_ma());
1125 let wt = abi::get_view_wt_for_proto(meta, resolve::navigation_transition_proto());
1126 assert!(!wt.is_null(), "AutomaticNavigationTransition: NavigationTransition");
1127 let value = abi::static_out(
1128 resolve::navigation_transition_automatic(), meta, abi::type_bytes(meta));
1129 generic1_modifier_ints(
1130 resolve::navigation_transition_fn(),
1131 resolve::navigation_transition_oret(),
1132 view,
1133 meta,
1134 wt,
1135 &[value.as_ptr() as u64],
1136 [0.0; 4],
1137 )
1138 }
1139}
1140
1141swift_statics! {
1142 pub enum LineHeightKind {
1148 Variable = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV8variableAFvgZ",
1149 Normal = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV6normalAFvgZ",
1150 Tight = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV5tightAFvgZ",
1151 Loose = c"$s10Foundation16AttributedStringV8CoreTextE10LineHeightV5looseAFvgZ",
1152 }
1153}
1154
1155pub fn line_height(view: &ViewHandle, height: Option<LineHeightKind>) -> ViewHandle {
1162 unsafe {
1163 let payload = abi::metadata0(resolve::line_height_ma());
1164 let opt = abi::resolve_type_by_mangled_name(
1165 b"10Foundation16AttributedStringV8CoreTextE10LineHeightVSg");
1166 assert!(!opt.is_null(), "Optional<AttributedString.LineHeight>");
1167 let mut buf = vec![0u8; abi::type_bytes(opt)];
1168 if let Some(kind) = height {
1169 let v = resilient_static(payload, kind.getter());
1170 buf[..v.len()].copy_from_slice(&v);
1171 }
1172 abi::store_optional_tag(&mut buf, payload, height.is_some());
1173 opaque_modifier(
1174 resolve::line_height_fn(),
1175 resolve::line_height_oret(),
1176 view,
1177 &[buf.as_ptr() as u64],
1178 [0.0; 4],
1179 )
1180 }
1181}
1182
1183pub fn refreshable(view: &ViewHandle, action: impl Fn() + Send + Sync + 'static) -> ViewHandle {
1188 unsafe {
1189 static mut RECORD: crate::asyncfn::FunctionPointer =
1190 crate::asyncfn::FunctionPointer::new();
1191 let closure = crate::asyncfn::prepare(
1192 crate::static_mut(&raw mut RECORD),
1193 0,
1194 action,
1195 );
1196 opaque_modifier(
1197 resolve::refreshable_fn(),
1198 resolve::refreshable_oret(),
1199 view,
1200 &closure,
1201 [0.0; 4],
1202 )
1203 }
1204}
1205
1206#[doc(hidden)]
1208pub unsafe fn generic1_modifier_ints_for_test(
1209 func: *const c_void,
1210 oret: *const c_void,
1211 view: &ViewHandle,
1212 arg_meta: *const c_void,
1213 arg_wt: *const c_void,
1214 leading: &[u64],
1215) -> ViewHandle {
1216 unsafe { generic1_modifier_ints(func, oret, view, arg_meta, arg_wt, leading, [0.0; 4]) }
1217}
1218
1219swift_statics! {
1220 pub enum TaskPriority {
1226 High = c"$sScP4highScPvgZ",
1227 UserInitiated = c"$sScP13userInitiatedScPvgZ",
1228 Utility = c"$sScP7utilityScPvgZ",
1229 Low = c"$sScP3lowScPvgZ",
1230 Background = c"$sScP10backgroundScPvgZ",
1231 }
1232}
1233
1234pub fn task(
1236 view: &ViewHandle,
1237 id: i64,
1238 priority: TaskPriority,
1239 file: &str,
1240 line: i64,
1241 action: impl Fn() + Send + Sync + 'static,
1242) -> ViewHandle {
1243 unsafe {
1244 static mut RECORD: crate::asyncfn::FunctionPointer =
1245 crate::asyncfn::FunctionPointer::new();
1246 let closure = crate::asyncfn::prepare(
1247 crate::static_mut(&raw mut RECORD),
1248 1,
1249 action,
1250 );
1251 let ctx = Box::leak(Box::new(abi::IsolatedContext::nonisolated()))
1252 as *mut abi::IsolatedContext;
1253 let pm = abi::metadata0(resolve::task_priority_ma());
1254 let p = resilient_static(pm, priority.getter());
1255 let name = abi::swift_string(&format!("View.task @ {file}:{line}"));
1256 let n0 = u64::from_le_bytes(name[..8].try_into().expect("name lo"));
1257 let n1 = u64::from_le_bytes(name[8..].try_into().expect("name hi"));
1258 let meta = resolve::int_meta();
1259 let wt = resolve::int_equatable_wt();
1260 let modifier = abi::generic_metadata2(resolve::task_modifier_ma(), meta, wt);
1261 assert!(!modifier.is_null(), "_TaskValueModifier2<Int> metadata");
1262 let mut value = vec![0u8; abi::type_bytes(modifier)];
1263 let mut boxed = id;
1264 core::arch::asm!(
1265 "sub sp, sp, #16",
1266 "stp {s0}, {s1}, [sp]",
1267 "blr {f}",
1268 "add sp, sp, #16",
1269 f = in(reg) resolve::task_modifier_init(),
1270 s0 = in(reg) meta, s1 = in(reg) wt,
1271 in("x0") &mut boxed as *mut i64,
1272 in("x1") n0, in("x2") n1,
1273 in("x3") 0u64, in("x4") 0u64,
1274 in("x5") p.as_ptr(),
1275 in("x6") closure[0], in("x7") ctx as u64,
1276 in("x8") value.as_mut_ptr(),
1277 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1278 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1279 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1280 clobber_abi("C"),
1281 );
1282 apply_modifier(view, modifier, &value)
1283 }
1284}
1285
1286pub fn task_once(
1297 view: &ViewHandle,
1298 priority: TaskPriority,
1299 file: &str,
1300 line: i64,
1301 action: impl Fn() + Send + Sync + 'static,
1302) -> ViewHandle {
1303 unsafe {
1304 static mut RECORD: crate::asyncfn::FunctionPointer =
1305 crate::asyncfn::FunctionPointer::new();
1306 let closure = crate::asyncfn::prepare(
1307 crate::static_mut(&raw mut RECORD),
1308 2,
1309 action,
1310 );
1311 let ctx = Box::leak(Box::new(abi::IsolatedContext::nonisolated()))
1312 as *mut abi::IsolatedContext;
1313 let pm = abi::metadata0(resolve::task_priority_ma());
1314 let p = resilient_static(pm, priority.getter());
1315 let name = abi::swift_string(&format!("View.task @ {file}:{line}"));
1316 let n0 = u64::from_le_bytes(name[..8].try_into().expect("name lo"));
1317 let n1 = u64::from_le_bytes(name[8..].try_into().expect("name hi"));
1318
1319 let modifier = abi::metadata0(resolve::task_modifier2_ma());
1320 assert!(!modifier.is_null(), "_TaskModifier2 metadata");
1321 let mut value = vec![0u8; abi::type_bytes(modifier)];
1322 core::arch::asm!("blr {f}", f = in(reg) resolve::task_modifier2_init(),
1326 in("x0") n0, in("x1") n1,
1327 in("x2") 0u64, in("x3") 0u64,
1328 in("x4") p.as_ptr(),
1329 in("x5") closure[0], in("x6") ctx as u64,
1330 in("x8") value.as_mut_ptr(),
1331 lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
1332 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
1333 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x20") _,
1334 lateout("x21") _, clobber_abi("C"),
1335 );
1336 apply_modifier(view, modifier, &value)
1337 }
1338}