use super::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::{self, swift_type};
mangle::modifier_types! {
pub(crate) ON_CHANGE_INNER_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("Si".into())]);
pub(crate) ON_CHANGE_STR_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("SS".into())]);
pub(crate) ON_CHANGE_BOOL_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("Sb".into())]);
pub(crate) ON_CHANGE_F64_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("Sd".into())]);
pub(crate) ON_CHANGE_OUTER_T = ty("_AppearanceActionModifier");
}
mangle::modifier_types! {
pub(crate) CLIPPED_T = ty("_ClipEffect").of(vec![ty("Rectangle")]);
pub(crate) COMPOSITING_T = ty("_CompositingGroupEffect");
pub(crate) DRAWING_GROUP_T = ty("_DrawingGroupEffect");
pub(crate) ROTATION3D_T = ty("_Rotation3DEffect");
pub(crate) DRAG_T = ty("AddGestureModifier").of(vec![ty("DragGesture"), ty("DefaultGestureCombiner")]);
pub(crate) MAGNIFY_T = ty("AddGestureModifier").of(vec![ty("MagnifyGesture"), ty("DefaultGestureCombiner")]);
pub(crate) ROTATE_T = ty("AddGestureModifier").of(vec![ty("RotateGesture"), ty("DefaultGestureCombiner")]);
pub(crate) POPOVER_T = ty("PopoverPresentationModifier").of(vec![ty("PopoverConditionalStateProvider").of(vec![ty("AnyView")])]);
}
pub fn clipped(view: &ViewHandle) -> ViewHandle {
unsafe {
let (_, _, size) = CLIPPED_T.applied_to(view.meta);
let r = abi::call_modifier(resolve::clipped_fn(), view.value_ptr(), view.meta, view.wt, &[0], [0.0; 4], size);
wrap_modified(r, &CLIPPED_T, view)
}
}
pub fn compositing_group(view: &ViewHandle) -> ViewHandle {
unsafe {
let (_, _, size) = COMPOSITING_T.applied_to(view.meta);
let r = abi::call_modifier(resolve::compositing_group_fn(), view.value_ptr(), view.meta, view.wt, &[], [0.0; 4], size);
wrap_modified(r, &COMPOSITING_T, view)
}
}
pub fn drawing_group(view: &ViewHandle) -> ViewHandle {
unsafe {
let (_, _, size) = DRAWING_GROUP_T.applied_to(view.meta);
let mut mode = [0u8]; let r = abi::call_modifier(resolve::drawing_group_fn(), view.value_ptr(), view.meta, view.wt,
&[0, mode.as_mut_ptr() as u64],
[0.0; 4],
size,
);
wrap_modified(r, &DRAWING_GROUP_T, view)
}
}
pub fn rotation_3d(view: &ViewHandle, degrees: f64, ax: f64, ay: f64, az: f64) -> ViewHandle {
unsafe {
let (_, _, size) = ROTATION3D_T.applied_to(view.meta);
let mut result = vec![0u8; size];
let this = view.value_ptr();
let radians = degrees * std::f64::consts::PI / 180.0;
let (anchor_x, anchor_y) = (0.5f64, 0.5f64);
core::arch::asm!("blr {f}", f = in(reg) resolve::rotation3d_fn(),
in("d0") radians,
in("d1") ax, in("d2") ay, in("d3") az,
in("d4") anchor_x, in("d5") anchor_y,
in("d6") 0.0f64, in("d7") 1.0f64,
in("x0") view.meta,
in("x1") view.wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
wrap_modified(result, &ROTATION3D_T, view)
}
}
pub(crate) unsafe fn attach_gesture(
view: &ViewHandle,
gesture: &mut [u8],
g_meta: *const c_void,
modifier: &ModifierType,
) -> ViewHandle {
unsafe {
let g_wt = abi::get_view_wt_for_proto(g_meta, resolve::gesture_proto());
assert!(!g_wt.is_null(), "gesture type: Gesture");
let (_, _, size) = modifier.applied_to(view.meta);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_fn(),
in("x0") gesture.as_mut_ptr(),
in("x1") 3u64, in("x2") view.meta,
in("x3") g_meta,
in("x4") view.wt,
in("x5") g_wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
wrap_modified(result, modifier, view)
}
}
pub fn drag_gesture(view: &ViewHandle, minimum_distance: f64) -> ViewHandle {
unsafe {
let g_meta = abi::metadata0(resolve::drag_gesture_ma());
let space_meta = abi::metadata0(resolve::local_space_ma());
let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
let mut space = [0u8; 8];
core::arch::asm!("blr {f}", f = in(reg) resolve::drag_gesture_init(),
in("d0") minimum_distance,
in("x0") space.as_mut_ptr(),
in("x1") space_meta,
in("x2") resolve::local_space_wt(),
in("x8") gesture.as_mut_ptr(),
in("x20") g_meta,
lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
clobber_abi("C"),
);
attach_gesture(view, &mut gesture, g_meta, &DRAG_T)
}
}
pub fn on_drag_gesture(view: &ViewHandle, minimum_distance: f64, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
drag_ended(
view,
minimum_distance,
invoke_value_action,
boxed_ctx(Box::new(action) as Box<dyn Fn()>),
)
}
}
pub fn on_drag_ended_value(
view: &ViewHandle,
minimum_distance: f64,
action: impl Fn(DragValue) + 'static,
) -> ViewHandle {
unsafe {
drag_ended(
view,
minimum_distance,
invoke_drag_value,
boxed_ctx(Box::new(action) as Box<dyn Fn(DragValue)>),
)
}
}
unsafe fn drag_ended(
view: &ViewHandle,
minimum_distance: f64,
body: unsafe extern "C" fn(*const u8, *mut c_void),
payload: *mut c_void,
) -> ViewHandle {
unsafe {
let g_meta = abi::metadata0(resolve::drag_gesture_ma());
let space_meta = abi::metadata0(resolve::local_space_ma());
let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
let mut space = [0u8; 8];
core::arch::asm!("blr {f}", f = in(reg) resolve::drag_gesture_init(),
in("d0") minimum_distance,
in("x0") space.as_mut_ptr(),
in("x1") space_meta,
in("x2") resolve::local_space_wt(),
in("x8") gesture.as_mut_ptr(),
in("x20") g_meta,
lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
clobber_abi("C"),
);
gesture_ended(view, &mut gesture, g_meta, body, payload)
}
}
pub(crate) unsafe fn gesture_ended(
view: &ViewHandle,
gesture: &mut [u8],
g_meta: *const c_void,
body: unsafe extern "C" fn(*const u8, *mut c_void),
payload: *mut c_void,
) -> ViewHandle {
unsafe {
let g_wt = abi::get_view_wt_for_proto(g_meta, resolve::gesture_proto());
assert!(!g_wt.is_null(), "gesture type: Gesture");
let ended_meta =
abi::generic_metadata2(resolve::ended_gesture_ma(), g_meta, g_wt);
let mut ended = vec![0u8; abi::type_bytes(ended_meta)];
let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(body, payload)))
as *mut abi::ValueClosureCtx;
core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_on_ended(),
in("x0") abi::value_closure_thunk as *const () as u64,
in("x1") ctx as u64,
in("x2") g_meta,
in("x3") g_wt,
in("x8") ended.as_mut_ptr(),
in("x20") gesture.as_mut_ptr(),
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
let ended_wt = abi::get_view_wt_for_proto(ended_meta, resolve::gesture_proto());
assert!(!ended_wt.is_null(), "_EndedGesture<G>: Gesture");
let subst = [
view.meta,
ended_meta,
view.wt,
ended_wt,
];
let (meta, wt, size) = opaque_result_type(resolve::gesture_oret(), &subst);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_fn(),
in("x0") ended.as_mut_ptr(),
in("x1") 3u64, in("x2") view.meta,
in("x3") ended_meta,
in("x4") view.wt,
in("x5") ended_wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub(crate) unsafe extern "C" fn invoke_value_action(_value: *const u8, payload: *mut c_void) {
let action = unsafe { boxed_ref::<dyn Fn()>(payload) };
action();
}
pub fn magnify_gesture(view: &ViewHandle, minimum_scale_delta: f64) -> ViewHandle {
unsafe {
let g_meta = abi::metadata0(resolve::magnify_gesture_ma());
let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::magnify_gesture_init(),
in("d0") minimum_scale_delta,
in("x8") gesture.as_mut_ptr(),
in("x20") g_meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
attach_gesture(view, &mut gesture, g_meta, &MAGNIFY_T)
}
}
pub fn rotate_gesture(view: &ViewHandle, minimum_degrees: f64) -> ViewHandle {
unsafe {
let g_meta = abi::metadata0(resolve::rotate_gesture_ma());
let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
let radians = minimum_degrees * std::f64::consts::PI / 180.0;
core::arch::asm!("blr {f}", f = in(reg) resolve::rotate_gesture_init(),
in("d0") radians,
in("x8") gesture.as_mut_ptr(),
in("x20") g_meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
attach_gesture(view, &mut gesture, g_meta, &ROTATE_T)
}
}
pub(crate) type ChangeHandler = Box<dyn Fn(i64, i64)>;
pub(crate) unsafe extern "C" fn invoke_change(old: *const u8, new: *const u8, payload: *mut c_void) {
let handler = unsafe { &*(payload as *const ChangeHandler) };
unsafe { handler(*(old as *const i64), *(new as *const i64)) };
}
pub fn on_change(
view: &ViewHandle,
value: i64,
handler: impl Fn(i64, i64) + 'static,
) -> ViewHandle {
unsafe {
let mut v = value;
on_change_of(
view,
(&raw mut v).cast(),
resolve::int_meta(),
&ON_CHANGE_INNER_T,
invoke_change,
boxed_ctx(Box::new(handler) as Box<dyn Fn(i64, i64)>),
)
}
}
pub fn on_change_str(
view: &ViewHandle,
value: &str,
handler: impl Fn(String, String) + 'static,
) -> ViewHandle {
unsafe {
let mut v = abi::swift_string(value);
on_change_of(
view,
v.as_mut_ptr(),
resolve::string_meta(),
&ON_CHANGE_STR_T,
invoke_change_str,
boxed_ctx(Box::new(handler) as Box<dyn Fn(String, String)>),
)
}
}
pub fn on_change_bool(
view: &ViewHandle,
value: bool,
handler: impl Fn(bool, bool) + 'static,
) -> ViewHandle {
unsafe {
let mut v = u8::from(value);
on_change_of(
view,
&raw mut v,
resolve::bool_meta(),
&ON_CHANGE_BOOL_T,
invoke_change_bool,
boxed_ctx(Box::new(handler) as Box<dyn Fn(bool, bool)>),
)
}
}
pub fn on_change_f64(
view: &ViewHandle,
value: f64,
handler: impl Fn(f64, f64) + 'static,
) -> ViewHandle {
unsafe {
let mut v = value;
on_change_of(
view,
(&raw mut v).cast(),
resolve::double_meta(),
&ON_CHANGE_F64_T,
invoke_change_f64,
boxed_ctx(Box::new(handler) as Box<dyn Fn(f64, f64)>),
)
}
}
pub(crate) unsafe extern "C" fn invoke_change_str(
old: *const u8,
new: *const u8,
payload: *mut c_void,
) {
unsafe {
let f = boxed_ref::<dyn Fn(String, String)>(payload);
let read = |p: *const u8| {
let mut b = [0u8; 16];
b.copy_from_slice(core::slice::from_raw_parts(p, 16));
abi::read_swift_string(b)
};
f(read(old), read(new));
}
}
pub(crate) unsafe extern "C" fn invoke_change_bool(
old: *const u8,
new: *const u8,
payload: *mut c_void,
) {
unsafe {
let f = boxed_ref::<dyn Fn(bool, bool)>(payload);
f(*old & 1 != 0, *new & 1 != 0);
}
}
pub(crate) unsafe extern "C" fn invoke_change_f64(
old: *const u8,
new: *const u8,
payload: *mut c_void,
) {
unsafe {
let f = boxed_ref::<dyn Fn(f64, f64)>(payload);
f(*(old as *const f64), *(new as *const f64));
}
}
unsafe fn on_change_of(
view: &ViewHandle,
value: *mut u8,
meta: *const c_void,
inner: &ModifierType,
body: unsafe extern "C" fn(*const u8, *const u8, *mut c_void),
payload: *mut c_void,
) -> ViewHandle {
unsafe {
let ctx = Box::leak(Box::new(abi::PairClosureCtx::new(body, payload)))
as *mut abi::PairClosureCtx;
let eq_wt = abi::get_view_wt_for_proto(meta, resolve::equatable_proto());
assert!(!eq_wt.is_null(), "{}: Equatable", crate::diag::type_name(meta));
let (inner_m, _, _) = inner.applied_to(view.meta);
let (_, _, size) = ON_CHANGE_OUTER_T.applied_to(inner_m);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::on_change_fn(),
in("x0") value,
in("x1") 0u64,
in("x2") abi::pair_closure_thunk as *const c_void,
in("x3") ctx,
in("x4") view.meta,
in("x5") meta,
in("x6") view.wt,
in("x7") eq_wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
let (nested, _, _) = inner.applied_to(view.meta);
let (meta, wt, _) = ON_CHANGE_OUTER_T.applied_to(nested);
wrap_owned(result, meta, wt)
}
}
pub(crate) unsafe fn popover_with_binding(
view: &ViewHandle,
content: &ViewHandle,
binding: &[u8],
) -> ViewHandle {
unsafe {
let b0 = u64::from_le_bytes(binding[0..8].try_into().unwrap());
let b1 = u64::from_le_bytes(binding[8..16].try_into().unwrap());
let b2 = binding[16] as u64;
let anchor_meta = abi::metadata0(resolve::popover_anchor_ma());
let mut anchor = vec![0u8; abi::type_bytes(anchor_meta)];
let source: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::anchor_source_bounds(),
lateout("x0") source,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
anchor[..8].copy_from_slice(&source.to_le_bytes());
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
emit_tuple,
Child::leak(content, Content::Erased),
))) as *mut abi::ClosureCtx;
let (_, _, size) = POPOVER_T.applied_to(view.meta);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!(
"sub sp, sp, #32",
"stp {sm}, {cm}, [sp]",
"stp {sw}, {cw}, [sp, #16]",
"blr {f}",
"add sp, sp, #32",
f = in(reg) resolve::popover_core_fn(),
sm = in(reg) view.meta,
cm = in(reg) resolve::anyview_meta(),
sw = in(reg) view.wt,
cw = in(reg) resolve::anyview_wt(),
in("x0") b0, in("x1") b1, in("x2") b2,
in("x3") anchor.as_mut_ptr(),
in("x4") 0x0100u64,
in("x5") 0u64,
in("x6") abi::swift_closure_thunk as *const c_void,
in("x7") ctx,
in("x8") result.as_mut_ptr(),
in("x20") this,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
wrap_modified(result, &POPOVER_T, view)
}
}
pub fn popover(view: &ViewHandle, content: &ViewHandle, presented: bool) -> ViewHandle {
unsafe {
let binding = constant_binding(
&[u8::from(presented)],
resolve::bool_meta(),
mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
);
popover_with_binding(view, content, &binding)
}
}
pub fn popover_bound(view: &ViewHandle, content: &ViewHandle, state: &BoundState<bool>) -> ViewHandle {
unsafe {
let binding = bool_binding_of(state);
popover_with_binding(view, content, &binding)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DragValue {
pub location: (f64, f64),
pub start_location: (f64, f64),
pub velocity: (f64, f64),
pub time: f64,
}
impl DragValue {
pub fn translation(&self) -> (f64, f64) {
(self.location.0 - self.start_location.0, self.location.1 - self.start_location.1)
}
}
fn drag_value_offsets() -> Option<&'static [usize; 4]> {
static CACHE: std::sync::OnceLock<Option<[usize; 4]>> = std::sync::OnceLock::new();
CACHE
.get_or_init(|| unsafe {
let meta = abi::metadata0(resolve::drag_value_ma());
let offs = abi::struct_field_offsets(meta)?;
if offs.len() != 5 {
return None;
}
Some([offs[0], offs[1], offs[2], offs[3]])
})
.as_ref()
}
pub(crate) unsafe fn read_drag_value(p: *const u8) -> DragValue {
let Some(&[time, location, start, velocity]) = drag_value_offsets() else {
return DragValue {
location: (0.0, 0.0),
start_location: (0.0, 0.0),
velocity: (0.0, 0.0),
time: 0.0,
};
};
unsafe {
let point = |at: usize| {
let f = p.add(at) as *const f64;
(*f, *f.add(1))
};
let v = DragValue {
time: *(p.add(time) as *const f64),
location: point(location),
start_location: point(start),
velocity: point(velocity),
};
debug_assert_eq!(
v.translation(),
swift_translation(p),
"computed translation disagrees with SwiftUI's own getter — the \
field offsets have moved"
);
v
}
}
unsafe fn swift_translation(p: *const u8) -> (f64, f64) {
unsafe {
let (w, h): (f64, f64);
core::arch::asm!("blr {f}", f = in(reg) resolve::drag_value_translation(),
in("x20") p,
lateout("d0") w, lateout("d1") h,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
(w, h)
}
}
pub(crate) unsafe extern "C" fn invoke_drag_value(value: *const u8, payload: *mut c_void) {
let f = unsafe { boxed_ref::<dyn Fn(DragValue)>(payload) };
f(unsafe { read_drag_value(value) });
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MagnifyValue {
pub magnification: f64,
pub velocity: f64,
pub start_anchor: (f64, f64),
pub start_location: (f64, f64),
pub time: f64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RotateValue {
pub radians: f64,
pub velocity: f64,
pub start_anchor: (f64, f64),
pub start_location: (f64, f64),
pub time: f64,
}
impl RotateValue {
pub fn degrees(&self) -> f64 {
self.radians.to_degrees()
}
}
fn pinch_value_offsets(accessor: *const c_void) -> Option<[usize; 5]> {
unsafe {
let offs = abi::struct_field_offsets(abi::metadata0(accessor))?;
if offs.len() != 6 {
return None;
}
Some([offs[0], offs[1], offs[2], offs[3], offs[4]])
}
}
unsafe fn read_pinch_value(p: *const u8, accessor: *const c_void) -> [f64; 7] {
let Some([time, amount, velocity, anchor, start]) = pinch_value_offsets(accessor) else {
return [0.0; 7];
};
unsafe {
let at = |o: usize| *(p.add(o) as *const f64);
let pair = |o: usize| (at(o), at(o + 8));
let (ax, ay) = pair(anchor);
let (sx, sy) = pair(start);
[at(amount), at(velocity), ax, ay, sx, sy, at(time)]
}
}
unsafe extern "C" fn invoke_magnify_value(value: *const u8, payload: *mut c_void) {
let f = unsafe { boxed_ref::<dyn Fn(MagnifyValue)>(payload) };
let v = unsafe { read_pinch_value(value, resolve::magnify_value_ma()) };
f(MagnifyValue {
magnification: v[0],
velocity: v[1],
start_anchor: (v[2], v[3]),
start_location: (v[4], v[5]),
time: v[6],
});
}
unsafe extern "C" fn invoke_rotate_value(value: *const u8, payload: *mut c_void) {
let f = unsafe { boxed_ref::<dyn Fn(RotateValue)>(payload) };
let v = unsafe { read_pinch_value(value, resolve::rotate_value_ma()) };
f(RotateValue {
radians: v[0],
velocity: v[1],
start_anchor: (v[2], v[3]),
start_location: (v[4], v[5]),
time: v[6],
});
}
pub fn on_magnify_ended(
view: &ViewHandle,
minimum_scale_delta: f64,
action: impl Fn(MagnifyValue) + 'static,
) -> ViewHandle {
unsafe {
let g_meta = abi::metadata0(resolve::magnify_gesture_ma());
let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::magnify_gesture_init(),
in("d0") minimum_scale_delta,
in("x8") gesture.as_mut_ptr(),
in("x20") g_meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
gesture_ended(
view,
&mut gesture,
g_meta,
invoke_magnify_value,
boxed_ctx(Box::new(action) as Box<dyn Fn(MagnifyValue)>),
)
}
}
pub fn on_rotate_ended(
view: &ViewHandle,
minimum_degrees: f64,
action: impl Fn(RotateValue) + 'static,
) -> ViewHandle {
unsafe {
let g_meta = abi::metadata0(resolve::rotate_gesture_ma());
let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
let radians = minimum_degrees * std::f64::consts::PI / 180.0;
core::arch::asm!("blr {f}", f = in(reg) resolve::rotate_gesture_init(),
in("d0") radians,
in("x8") gesture.as_mut_ptr(),
in("x20") g_meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
gesture_ended(
view,
&mut gesture,
g_meta,
invoke_rotate_value,
boxed_ctx(Box::new(action) as Box<dyn Fn(RotateValue)>),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pinch_reader_matches_the_live_layout() {
for accessor in [resolve::magnify_value_ma(), resolve::rotate_value_ma()] {
let offsets = pinch_value_offsets(accessor).expect("six stored fields");
let [time, amount, velocity, anchor, start] = offsets;
let size = unsafe { abi::type_bytes(abi::metadata0(accessor)) };
let mut buf = vec![0u8; size];
let put = |b: &mut [u8], at: usize, v: f64| {
b[at..at + 8].copy_from_slice(&v.to_le_bytes());
};
put(&mut buf, time, 11.0);
put(&mut buf, amount, 22.0);
put(&mut buf, velocity, 33.0);
put(&mut buf, anchor, 44.0);
put(&mut buf, anchor + 8, 55.0);
put(&mut buf, start, 66.0);
put(&mut buf, start + 8, 77.0);
let got = unsafe { read_pinch_value(buf.as_ptr(), accessor) };
assert_eq!(got, [22.0, 33.0, 44.0, 55.0, 66.0, 77.0, 11.0]);
}
}
#[test]
fn rotate_value_converts_to_degrees() {
let v = RotateValue {
radians: std::f64::consts::FRAC_PI_2,
velocity: 0.0,
start_anchor: (0.5, 0.5),
start_location: (0.0, 0.0),
time: 0.0,
};
assert!((v.degrees() - 90.0).abs() < 1e-9);
}
#[test]
fn drag_value_translation_is_the_difference() {
let v = DragValue {
location: (142.0, 40.0),
start_location: (70.0, 40.0),
velocity: (164.0, 0.0),
time: 0.0,
};
assert_eq!(v.translation(), (72.0, 0.0));
}
}