1use super::{wrap_owned, ViewHandle};
37use crate::abi;
38use crate::resolve;
39use core::ffi::c_void;
40use std::sync::{Mutex, OnceLock};
41
42#[derive(Clone, Copy, Debug, PartialEq)]
44pub struct Rect {
45 pub x: f64,
46 pub y: f64,
47 pub width: f64,
48 pub height: f64,
49}
50
51#[derive(Clone, Copy, Debug, PartialEq)]
56pub struct Proposal {
57 pub width: Option<f64>,
58 pub height: Option<f64>,
59}
60
61impl Proposal {
62 pub fn or(self, fallback: f64) -> (f64, f64) {
64 (self.width.unwrap_or(fallback), self.height.unwrap_or(fallback))
65 }
66}
67
68pub struct Subviews<'a> {
74 raw: *mut c_void,
75 marker: core::marker::PhantomData<&'a ()>,
76}
77
78impl Subviews<'_> {
79 pub fn len(&self) -> usize {
81 unsafe {
82 let start = call_index(resolve::layout_subviews_start(), self.raw);
83 let end = call_index(resolve::layout_subviews_end(), self.raw);
84 (end - start).max(0) as usize
85 }
86 }
87
88 pub fn is_empty(&self) -> bool {
90 self.len() == 0
91 }
92
93 pub fn size_that_fits(&self, i: usize, proposal: Proposal) -> (f64, f64) {
99 unsafe {
100 let mut sub = self.child(i);
101 let (w, wt, h, ht) = proposal_words(proposal);
102 let (rw, rh): (f64, f64);
103 core::arch::asm!("blr {f}", f = in(reg) resolve::layout_subview_size(),
104 in("x0") w, in("x1") wt, in("x2") h, in("x3") ht,
105 in("x20") sub.as_mut_ptr(),
106 lateout("d0") rw, lateout("d1") rh,
107 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
108 lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
109 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
110 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
111 clobber_abi("C"),
112 );
113 (rw, rh)
114 }
115 }
116
117 pub fn place(&self, i: usize, x: f64, y: f64) {
119 self.place_proposed(i, x, y, Proposal { width: None, height: None });
120 }
121
122 pub fn place_proposed(&self, i: usize, x: f64, y: f64, proposal: Proposal) {
124 unsafe {
125 let mut sub = self.child(i);
126 let (w, wt, h, ht) = proposal_words(proposal);
127 core::arch::asm!("blr {f}", f = in(reg) resolve::layout_subview_place(),
132 in("d0") x, in("d1") y, in("d2") 0.0f64, in("d3") 0.0f64,
133 in("x0") w, in("x1") wt, in("x2") h, in("x3") ht,
134 in("x20") sub.as_mut_ptr(),
135 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
136 lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
137 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
138 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
139 clobber_abi("C"),
140 );
141 }
142 }
143
144 unsafe fn child(&self, i: usize) -> Vec<u8> {
149 unsafe {
150 let meta = abi::metadata0(resolve::layout_subview_ma());
151 assert!(!meta.is_null(), "LayoutSubview metadata");
152 let mut out = vec![0u8; abi::type_bytes(meta)];
153 core::arch::asm!("blr {f}", f = in(reg) resolve::layout_subviews_subscript(),
154 in("x0") i as i64,
155 in("x8") out.as_mut_ptr(),
156 in("x20") self.raw,
157 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
158 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
159 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
160 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
161 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
162 );
163 out
164 }
165 }
166}
167
168unsafe fn call_index(f: *const c_void, raw: *mut c_void) -> i64 {
173 unsafe {
174 let out: i64;
175 core::arch::asm!("blr {f}", f = in(reg) f,
176 in("x20") raw,
177 lateout("x0") out,
178 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
179 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
180 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
181 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
182 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
183 );
184 out
185 }
186}
187
188fn proposal_words(p: Proposal) -> (u64, u64, u64, u64) {
194 let one = |v: Option<f64>| match v {
195 Some(x) => (x.to_bits(), 0u64),
196 None => (0u64, 1u64),
197 };
198 let (w, wt) = one(p.width);
199 let (h, ht) = one(p.height);
200 (w, wt, h, ht)
201}
202
203pub trait LayoutAlgorithm: Send + Sync + 'static {
235 fn size_that_fits(&self, subviews: &Subviews<'_>, proposal: Proposal) -> (f64, f64);
237
238 fn place_subviews(&self, subviews: &Subviews<'_>, bounds: Rect, proposal: Proposal);
240}
241
242pub struct FnLayout<S, P> {
248 size: S,
249 place: P,
250}
251
252impl<S, P> LayoutAlgorithm for FnLayout<S, P>
253where
254 S: Fn(&Subviews<'_>, Proposal) -> (f64, f64) + Send + Sync + 'static,
255 P: Fn(&Subviews<'_>, Rect, Proposal) + Send + Sync + 'static,
256{
257 fn size_that_fits(&self, subviews: &Subviews<'_>, proposal: Proposal) -> (f64, f64) {
258 (self.size)(subviews, proposal)
259 }
260
261 fn place_subviews(&self, subviews: &Subviews<'_>, bounds: Rect, proposal: Proposal) {
262 (self.place)(subviews, bounds, proposal)
263 }
264}
265
266fn registry() -> &'static Mutex<Vec<&'static dyn LayoutAlgorithm>> {
267 static R: OnceLock<Mutex<Vec<&'static dyn LayoutAlgorithm>>> = OnceLock::new();
268 R.get_or_init(|| Mutex::new(Vec::new()))
269}
270
271pub struct CustomLayout {
278 index: i64,
279}
280
281pub fn layout(
286 size: impl Fn(&Subviews<'_>, Proposal) -> (f64, f64) + Send + Sync + 'static,
287 place: impl Fn(&Subviews<'_>, Rect, Proposal) + Send + Sync + 'static,
288) -> CustomLayout {
289 register(FnLayout { size, place })
290}
291
292pub fn register<L: LayoutAlgorithm>(algorithm: L) -> CustomLayout {
297 let leaked: &'static dyn LayoutAlgorithm = Box::leak(Box::new(algorithm));
298 let mut r = registry().lock().expect("layout registry");
299 r.push(leaked);
300 CustomLayout { index: r.len() as i64 - 1 }
301}
302
303unsafe fn algorithm_at(this: *const u8) -> &'static dyn LayoutAlgorithm {
308 let index = unsafe { *(this as *const i64) } as usize;
309 let r = registry().lock().expect("layout registry");
310 r.get(index).copied().expect("layout index out of range")
311}
312
313core::arch::global_asm!(
330 ".p2align 2",
331 ".globl _rswift_layout_size_thunk",
332 "_rswift_layout_size_thunk:",
333 "mov x6, x20",
334 "b _rswift_layout_size_that_fits",
335 ".p2align 2",
336 ".globl _rswift_layout_place_thunk",
337 "_rswift_layout_place_thunk:",
338 "mov x6, x20",
339 "b _rswift_layout_place_subviews",
340);
341
342unsafe extern "C" {
343 #[link_name = "rswift_layout_size_thunk"]
344 fn size_that_fits_thunk();
345 #[link_name = "rswift_layout_place_thunk"]
346 fn place_subviews_thunk();
347}
348
349unsafe extern "C" fn make_cache() {}
357
358#[repr(C)]
364struct Size {
365 width: f64,
366 height: f64,
367}
368
369#[unsafe(no_mangle)]
375unsafe extern "C" fn rswift_layout_size_that_fits(
376 w: u64,
377 wt: u64,
378 h: u64,
379 ht: u64,
380 subviews: *mut c_void,
381 _cache: *mut c_void,
382 this: *const u8,
383) -> Size {
384 let algorithm = unsafe { algorithm_at(this) };
385 let subviews = Subviews { raw: subviews, marker: core::marker::PhantomData };
386 let proposal = Proposal {
387 width: (wt == 0).then(|| f64::from_bits(w)),
388 height: (ht == 0).then(|| f64::from_bits(h)),
389 };
390 let (w, h) = algorithm.size_that_fits(&subviews, proposal);
391 Size { width: w, height: h }
392}
393
394#[unsafe(no_mangle)]
401unsafe extern "C" fn rswift_layout_place_subviews(
402 bx: f64,
403 by: f64,
404 bw: f64,
405 bh: f64,
406 w: u64,
407 wt: u64,
408 h: u64,
409 ht: u64,
410 subviews: *mut c_void,
411 _cache: *mut c_void,
412 this: *const u8,
413) {
414 let algorithm = unsafe { algorithm_at(this) };
415 let subviews = Subviews { raw: subviews, marker: core::marker::PhantomData };
416 let proposal = Proposal {
417 width: (wt == 0).then(|| f64::from_bits(w)),
418 height: (ht == 0).then(|| f64::from_bits(h)),
419 };
420 algorithm.place_subviews(&subviews, Rect { x: bx, y: by, width: bw, height: bh }, proposal);
421}
422
423fn carrier() -> (*const c_void, *const c_void) {
427 static C: OnceLock<(usize, usize)> = OnceLock::new();
428 let (d, m) = *C.get_or_init(|| unsafe {
429 let (d, m) = crate::state::emit_int_carrier(b"RustLayout\0", b"6rswift10RustLayoutV\0");
430 (d as usize, m as usize)
431 });
432 (d as *const c_void, m as *const c_void)
433}
434
435fn conformance() -> &'static crate::conformance::Conformance {
437 use crate::conformance::{synthesize, Protocol, Witness};
438 static C: OnceLock<crate::conformance::Conformance> = OnceLock::new();
439 C.get_or_init(|| unsafe {
440 let protocol = Protocol::from_symbol(c"$s7SwiftUI6LayoutMp");
441 assert_eq!(protocol.requirement_count(), 11, "Layout changed shape");
442 synthesize(
443 &protocol,
444 carrier().0,
449 &[
450 Witness::Metadata(resolve::hstack_layout_animatable_wt()),
451 Witness::Default,
452 Witness::Default,
453 Witness::Function(make_cache as *const c_void),
454 Witness::Default,
455 Witness::Default,
456 Witness::Function(size_that_fits_thunk as *const c_void),
457 Witness::Function(place_subviews_thunk as *const c_void),
458 Witness::Default,
459 Witness::Default,
460 Witness::Default,
461 ],
462 )
463 })
464}
465
466pub fn custom_layout(layout: &CustomLayout, children: &[ViewHandle]) -> ViewHandle {
468 unsafe {
469 let conformance = conformance();
470 let (_, carrier_meta) = carrier();
471
472 let mut value = vec![0u8; abi::type_bytes(carrier_meta)];
474 value[..8].copy_from_slice(&layout.index.to_le_bytes());
475
476 let root_meta = abi::generic_metadata2(
478 resolve::layout_root_ma(),
479 carrier_meta,
480 conformance.witness_table(),
481 );
482 assert!(!root_meta.is_null(), "_LayoutRoot metadata");
483 let mut root = vec![0u8; abi::type_bytes(root_meta)];
484 core::arch::asm!("blr {f}", f = in(reg) resolve::layout_root_init(),
485 in("x0") value.as_mut_ptr(),
486 in("x1") carrier_meta,
487 in("x2") conformance.witness_table(),
488 in("x8") root.as_mut_ptr(),
489 lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
490 lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
491 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
492 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x20") _,
493 lateout("x21") _, clobber_abi("C"),
494 );
495
496 let (content_meta, _content_wt, offsets) = if children.len() == 1 {
500 (children[0].meta, children[0].wt, vec![0usize])
501 } else {
502 let elements: Vec<*const c_void> = children.iter().map(|c| c.meta).collect();
503 let tuple = abi::tuple_metadata(&elements);
504 assert!(!tuple.is_null(), "tuple metadata instantiation failed");
505 let offsets = abi::tuple_element_offsets(tuple, children.len());
506 let m = abi::generic_metadata1(resolve::tupleview_ma(), tuple);
507 assert!(!m.is_null(), "TupleView metadata instantiation failed");
508 let w = abi::get_view_wt(m);
509 assert!(!w.is_null(), "TupleView !: View");
510 (m, w, offsets)
511 };
512 let kids: Vec<super::TupleChild> = children
513 .iter()
514 .zip(offsets.iter())
515 .map(|(c, &offset)| super::TupleChild {
516 value: c.value_ptr(),
517 meta: c.meta,
518 offset,
519 })
520 .collect();
521 let payload = super::TuplePayload { children: kids.as_ptr(), count: kids.len() };
522 let mut ctx = abi::ClosureCtx::new(
523 super::emit_tuple,
524 &payload as *const super::TuplePayload as *mut c_void,
525 );
526
527 let root_wt = super::conformance_wt(root_meta, resolve::variadic_root_proto());
528
529 let meta = abi::generic_metadata_from_descriptor(
532 resolve::variadic_tree_desc(),
533 &[root_meta, content_meta, root_wt],
534 );
535 assert!(!meta.is_null(), "_VariadicView.Tree metadata");
536 let wt = abi::get_view_wt(meta);
537 assert!(!wt.is_null(), "_VariadicView.Tree !: View");
538
539 let mut result = vec![0u8; abi::type_bytes(meta)];
540 core::arch::asm!("blr {f}", f = in(reg) resolve::variadic_tree_init(),
543 in("x0") root.as_mut_ptr(),
544 in("x1") abi::swift_closure_thunk as *const c_void,
545 in("x2") &mut ctx as *mut abi::ClosureCtx,
546 in("x3") root_meta,
547 in("x4") content_meta,
548 in("x5") root_wt,
549 in("x8") result.as_mut_ptr(),
550 lateout("x6") _, lateout("x7") _,
551 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
552 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
553 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
554 clobber_abi("C"),
555 );
556 wrap_owned(result, meta, wt)
557 }
558}