use super::{wrap_owned, PathBuilder, ViewHandle};
use crate::abi;
use crate::conformance::{synthesize, Conformance, Protocol, Witness};
use crate::resolve;
use core::ffi::c_void;
use std::sync::{Mutex, OnceLock};
pub use super::Rect;
pub struct ShapePath {
builder: PathBuilder,
}
impl ShapePath {
pub fn move_to(&mut self, x: f64, y: f64) -> &mut Self {
self.builder.move_to(x, y);
self
}
pub fn line_to(&mut self, x: f64, y: f64) -> &mut Self {
self.builder.line_to(x, y);
self
}
pub fn quad_curve_to(&mut self, x: f64, y: f64, cx: f64, cy: f64) -> &mut Self {
self.builder.quad_curve_to(x, y, cx, cy);
self
}
pub fn close(&mut self) -> &mut Self {
self.builder.close();
self
}
}
pub trait Shaping: Send + Sync + 'static {
fn path(&self, path: &mut ShapePath, rect: Rect);
}
impl<F> Shaping for F
where
F: Fn(&mut ShapePath, Rect) + Send + Sync + 'static,
{
fn path(&self, path: &mut ShapePath, rect: Rect) {
self(path, rect)
}
}
pub struct CustomShape {
index: i64,
}
fn registry() -> &'static Mutex<Vec<&'static dyn Shaping>> {
static R: OnceLock<Mutex<Vec<&'static dyn Shaping>>> = OnceLock::new();
R.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn shape_fn<S: Shaping>(shape: S) -> CustomShape {
let leaked: &'static dyn Shaping = Box::leak(Box::new(shape));
let mut r = registry().lock().expect("shape registry");
r.push(leaked);
CustomShape { index: r.len() as i64 - 1 }
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_shape_path_thunk",
"_rswift_shape_path_thunk:",
"mov x1, x20",
"mov x0, x8",
"b _rswift_shape_path",
);
unsafe extern "C" {
#[link_name = "rswift_shape_path_thunk"]
fn shape_path_thunk();
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_shape_path(
out: *mut u8,
this: *const u8,
x: f64,
y: f64,
width: f64,
height: f64,
) {
let index = unsafe { *(this as *const i64) } as usize;
let shape = {
let r = registry().lock().expect("shape registry");
r.get(index).copied().expect("shape index out of range")
};
let mut path = ShapePath { builder: super::Shape::path() };
shape.path(&mut path, Rect { x, y, width, height });
unsafe { path.builder.move_into(out) };
}
#[unsafe(no_mangle)]
static mut RSWIFT_SHAPE_WT: *const c_void = core::ptr::null();
crate::thunk::runtime_thunk! {
fn shape_body_thunk = "rswift_shape_body" via RSWIFT_SHAPE_BODY {
prologue: [
"adrp x1, _RSWIFT_SHAPE_WT@PAGE",
"add x1, x1, _RSWIFT_SHAPE_WT@PAGEOFF",
"ldr x1, [x1]",
],
branch: "br",
epilogue: [],
}
}
fn carrier() -> (*const c_void, *const c_void) {
static C: OnceLock<(usize, usize)> = OnceLock::new();
let (d, m) = *C.get_or_init(|| unsafe {
let (d, m) = crate::state::emit_int_carrier(b"RustShape\0", b"6rswift9RustShapeV\0");
(d as usize, m as usize)
});
(d as *const c_void, m as *const c_void)
}
fn shape_conformance() -> &'static Conformance {
static C: OnceLock<Conformance> = OnceLock::new();
C.get_or_init(|| unsafe {
let carrier_meta = carrier().1;
let animatable = Protocol::from_symbol(c"$s7SwiftUI10AnimatableMp");
assert_eq!(
animatable.requirement_count(),
6,
"Animatable changed shape — it used to be AnimatableData, its \
conformance, a getter, a setter, a modify coroutine and one default",
);
let borrowed = resolve::rectangle_animatable_wt() as *const *const c_void;
let witnesses: Vec<Witness> = (0..animatable.requirement_count())
.map(|i| Witness::Metadata(*borrowed.add(1 + i)))
.collect();
let animatable_wt = Box::leak(Box::new(synthesize(
&animatable,
carrier().0,
&witnesses,
)))
.witness_table();
let view_wt = abi::get_view_wt(carrier_meta);
assert!(
!view_wt.is_null(),
"the carrier type has no View conformance — `emit_int_carrier` used to \
register one",
);
let protocol = Protocol::from_symbol(c"$s7SwiftUI5ShapeMp");
assert_eq!(
protocol.requirement_count(),
6,
"Shape changed shape — it used to be Animatable, View, `path(in:)` and \
three defaulted requirements",
);
let shape = synthesize(
&protocol,
carrier().0,
&[
Witness::Metadata(animatable_wt),
Witness::Metadata(view_wt),
Witness::Function(shape_path_thunk as *const c_void),
Witness::Default,
Witness::Default,
Witness::Default,
],
);
RSWIFT_SHAPE_WT = shape.witness_table();
let style_meta = abi::metadata0(resolve::foreground_style_ma());
assert!(!style_meta.is_null(), "ForegroundStyle metadata");
let body_meta = abi::generic_metadata_from_descriptor(
resolve::shape_view_desc(),
&[carrier_meta, style_meta, shape.witness_table(), resolve::foreground_style_wt()],
);
assert!(
!body_meta.is_null(),
"_ShapeView<RustShape, ForegroundStyle> could not be instantiated",
);
let body_wt = abi::get_view_wt(body_meta);
assert!(!body_wt.is_null(), "{} !: View", crate::diag::type_name(body_meta));
RSWIFT_SHAPE_BODY = resolve::shape_extension_body();
let words = view_wt as *mut *const c_void;
*words.add(1) = body_wt;
*words.add(2) = body_meta;
*words.add(6) = shape_body_thunk as *const c_void;
shape
})
}
pub fn custom_shape(shape: &CustomShape) -> ViewHandle {
unsafe {
let conformance = shape_conformance();
let meta = carrier().1;
let wt = abi::get_view_wt(meta);
assert!(
!wt.is_null(),
"{} !: View — the synthesised conformance was not found",
crate::diag::type_name(meta),
);
let _ = conformance;
let mut bytes = vec![0u8; abi::type_bytes(meta)];
bytes[..8].copy_from_slice(&shape.index.to_le_bytes());
wrap_owned(bytes, meta, wt)
}
}
pub fn custom_shape_value(shape: &CustomShape) -> super::Shape {
unsafe {
let conformance = shape_conformance();
let meta = carrier().1;
let mut bytes = vec![0u8; abi::type_bytes(meta)];
bytes[..8].copy_from_slice(&shape.index.to_le_bytes());
super::Shape::of_metadata(
bytes,
meta,
conformance.witness_table(),
crate::mangle::Type::Verbatim("6rswift9RustShapeV".into()),
)
}
}