1use super::*;
4use crate::abi;
5use crate::resolve;
6use core::ffi::c_void;
7use crate::mangle::{self, swift_type};
8mangle::modifier_types! {
9 pub(crate) ON_CHANGE_INNER_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("Si".into())]);
12 pub(crate) ON_CHANGE_STR_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("SS".into())]);
14 pub(crate) ON_CHANGE_BOOL_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("Sb".into())]);
16 pub(crate) ON_CHANGE_F64_T = ty("_ValueActionModifier2").of(vec![Type::Verbatim("Sd".into())]);
18 pub(crate) ON_CHANGE_OUTER_T = ty("_AppearanceActionModifier");
20}
21mangle::modifier_types! {
22 pub(crate) CLIPPED_T = ty("_ClipEffect").of(vec![ty("Rectangle")]);
24 pub(crate) COMPOSITING_T = ty("_CompositingGroupEffect");
26 pub(crate) DRAWING_GROUP_T = ty("_DrawingGroupEffect");
28 pub(crate) ROTATION3D_T = ty("_Rotation3DEffect");
30 pub(crate) DRAG_T = ty("AddGestureModifier").of(vec![ty("DragGesture"), ty("DefaultGestureCombiner")]);
32 pub(crate) MAGNIFY_T = ty("AddGestureModifier").of(vec![ty("MagnifyGesture"), ty("DefaultGestureCombiner")]);
34 pub(crate) ROTATE_T = ty("AddGestureModifier").of(vec![ty("RotateGesture"), ty("DefaultGestureCombiner")]);
36 pub(crate) POPOVER_T = ty("PopoverPresentationModifier").of(vec![ty("PopoverConditionalStateProvider").of(vec![ty("AnyView")])]);
39}
40
41
42
43
44
45
46
47pub fn clipped(view: &ViewHandle) -> ViewHandle {
49 unsafe {
50 let (_, _, size) = CLIPPED_T.applied_to(view.meta);
51 let r = abi::call_modifier(resolve::clipped_fn(), view.value_ptr(), view.meta, view.wt, &[0], [0.0; 4], size);
52 wrap_modified(r, &CLIPPED_T, view)
53 }
54}
55
56pub fn compositing_group(view: &ViewHandle) -> ViewHandle {
58 unsafe {
59 let (_, _, size) = COMPOSITING_T.applied_to(view.meta);
60 let r = abi::call_modifier(resolve::compositing_group_fn(), view.value_ptr(), view.meta, view.wt, &[], [0.0; 4], size);
61 wrap_modified(r, &COMPOSITING_T, view)
62 }
63}
64
65pub fn drawing_group(view: &ViewHandle) -> ViewHandle {
67 unsafe {
68 let (_, _, size) = DRAWING_GROUP_T.applied_to(view.meta);
69 let mut mode = [0u8]; let r = abi::call_modifier(resolve::drawing_group_fn(), view.value_ptr(), view.meta, view.wt,
73 &[0, mode.as_mut_ptr() as u64],
74 [0.0; 4],
75 size,
76 );
77 wrap_modified(r, &DRAWING_GROUP_T, view)
78 }
79}
80
81pub fn rotation_3d(view: &ViewHandle, degrees: f64, ax: f64, ay: f64, az: f64) -> ViewHandle {
83 unsafe {
84 let (_, _, size) = ROTATION3D_T.applied_to(view.meta);
85 let mut result = vec![0u8; size];
86 let this = view.value_ptr();
87 let radians = degrees * std::f64::consts::PI / 180.0;
88 let (anchor_x, anchor_y) = (0.5f64, 0.5f64);
89 core::arch::asm!("blr {f}", f = in(reg) resolve::rotation3d_fn(),
93 in("d0") radians,
94 in("d1") ax, in("d2") ay, in("d3") az,
95 in("d4") anchor_x, in("d5") anchor_y,
96 in("d6") 0.0f64, in("d7") 1.0f64,
97 in("x0") view.meta,
98 in("x1") view.wt,
99 in("x8") result.as_mut_ptr(),
100 in("x20") this,
101 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
102 lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
103 lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
104 lateout("x15") _, lateout("x16") _, lateout("x17") _,
105 lateout("lr") _, lateout("x21") _, clobber_abi("C"),
106 );
107 wrap_modified(result, &ROTATION3D_T, view)
108 }
109}
110
111pub(crate) unsafe fn attach_gesture(
113 view: &ViewHandle,
114 gesture: &mut [u8],
115 g_meta: *const c_void,
116 modifier: &ModifierType,
117) -> ViewHandle {
118 unsafe {
119 let g_wt = abi::get_view_wt_for_proto(g_meta, resolve::gesture_proto());
120 assert!(!g_wt.is_null(), "gesture type: Gesture");
121 let (_, _, size) = modifier.applied_to(view.meta);
122 let mut result = vec![0u8; size];
123 let this = view.value_ptr();
124 core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_fn(),
125 in("x0") gesture.as_mut_ptr(),
126 in("x1") 3u64, in("x2") view.meta,
128 in("x3") g_meta,
129 in("x4") view.wt,
130 in("x5") g_wt,
131 in("x8") result.as_mut_ptr(),
132 in("x20") this,
133 lateout("x6") _, lateout("x7") _,
134 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
135 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
136 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
137 );
138 wrap_modified(result, modifier, view)
139 }
140}
141
142pub fn drag_gesture(view: &ViewHandle, minimum_distance: f64) -> ViewHandle {
144 unsafe {
145 let g_meta = abi::metadata0(resolve::drag_gesture_ma());
146 let space_meta = abi::metadata0(resolve::local_space_ma());
147 let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
148 let mut space = [0u8; 8];
152 core::arch::asm!("blr {f}", f = in(reg) resolve::drag_gesture_init(),
153 in("d0") minimum_distance,
154 in("x0") space.as_mut_ptr(),
155 in("x1") space_meta,
156 in("x2") resolve::local_space_wt(),
157 in("x8") gesture.as_mut_ptr(),
158 in("x20") g_meta,
159 lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
160 lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
161 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
162 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
163 clobber_abi("C"),
164 );
165 attach_gesture(view, &mut gesture, g_meta, &DRAG_T)
166 }
167}
168
169pub fn on_drag_gesture(view: &ViewHandle, minimum_distance: f64, action: impl Fn() + 'static) -> ViewHandle {
180 unsafe {
181 drag_ended(
182 view,
183 minimum_distance,
184 invoke_value_action,
185 boxed_ctx(Box::new(action) as Box<dyn Fn()>),
186 )
187 }
188}
189
190pub fn on_drag_ended_value(
196 view: &ViewHandle,
197 minimum_distance: f64,
198 action: impl Fn(DragValue) + 'static,
199) -> ViewHandle {
200 unsafe {
201 drag_ended(
202 view,
203 minimum_distance,
204 invoke_drag_value,
205 boxed_ctx(Box::new(action) as Box<dyn Fn(DragValue)>),
206 )
207 }
208}
209
210unsafe fn drag_ended(
215 view: &ViewHandle,
216 minimum_distance: f64,
217 body: unsafe extern "C" fn(*const u8, *mut c_void),
218 payload: *mut c_void,
219) -> ViewHandle {
220 unsafe {
221 let g_meta = abi::metadata0(resolve::drag_gesture_ma());
222 let space_meta = abi::metadata0(resolve::local_space_ma());
223 let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
224 let mut space = [0u8; 8];
225 core::arch::asm!("blr {f}", f = in(reg) resolve::drag_gesture_init(),
226 in("d0") minimum_distance,
227 in("x0") space.as_mut_ptr(),
228 in("x1") space_meta,
229 in("x2") resolve::local_space_wt(),
230 in("x8") gesture.as_mut_ptr(),
231 in("x20") g_meta,
232 lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
233 lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
234 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
235 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
236 clobber_abi("C"),
237 );
238 gesture_ended(view, &mut gesture, g_meta, body, payload)
239 }
240}
241
242pub(crate) unsafe fn gesture_ended(
255 view: &ViewHandle,
256 gesture: &mut [u8],
257 g_meta: *const c_void,
258 body: unsafe extern "C" fn(*const u8, *mut c_void),
259 payload: *mut c_void,
260) -> ViewHandle {
261 unsafe {
262 let g_wt = abi::get_view_wt_for_proto(g_meta, resolve::gesture_proto());
263 assert!(!g_wt.is_null(), "gesture type: Gesture");
264
265 let ended_meta =
268 abi::generic_metadata2(resolve::ended_gesture_ma(), g_meta, g_wt);
269 let mut ended = vec![0u8; abi::type_bytes(ended_meta)];
270
271 let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(body, payload)))
274 as *mut abi::ValueClosureCtx;
275
276 core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_on_ended(),
279 in("x0") abi::value_closure_thunk as *const () as u64,
280 in("x1") ctx as u64,
281 in("x2") g_meta,
282 in("x3") g_wt,
283 in("x8") ended.as_mut_ptr(),
284 in("x20") gesture.as_mut_ptr(),
285 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
286 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
287 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
288 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
289 );
290
291 let ended_wt = abi::get_view_wt_for_proto(ended_meta, resolve::gesture_proto());
292 assert!(!ended_wt.is_null(), "_EndedGesture<G>: Gesture");
293
294 let subst = [
295 view.meta,
296 ended_meta,
297 view.wt,
298 ended_wt,
299 ];
300 let (meta, wt, size) = opaque_result_type(resolve::gesture_oret(), &subst);
301 let mut result = vec![0u8; size];
302 let this = view.value_ptr();
303 core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_fn(),
304 in("x0") ended.as_mut_ptr(),
305 in("x1") 3u64, in("x2") view.meta,
307 in("x3") ended_meta,
308 in("x4") view.wt,
309 in("x5") ended_wt,
310 in("x8") result.as_mut_ptr(),
311 in("x20") this,
312 lateout("x6") _, lateout("x7") _,
313 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
314 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
315 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
316 );
317 wrap_owned(result, meta, wt)
318 }
319}
320
321pub(crate) unsafe extern "C" fn invoke_value_action(_value: *const u8, payload: *mut c_void) {
323 let action = unsafe { boxed_ref::<dyn Fn()>(payload) };
324 action();
325}
326
327pub fn magnify_gesture(view: &ViewHandle, minimum_scale_delta: f64) -> ViewHandle {
329 unsafe {
330 let g_meta = abi::metadata0(resolve::magnify_gesture_ma());
331 let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
332 core::arch::asm!("blr {f}", f = in(reg) resolve::magnify_gesture_init(),
333 in("d0") minimum_scale_delta,
334 in("x8") gesture.as_mut_ptr(),
335 in("x20") g_meta,
336 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
337 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
338 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
339 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
340 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
341 );
342 attach_gesture(view, &mut gesture, g_meta, &MAGNIFY_T)
343 }
344}
345
346pub fn rotate_gesture(view: &ViewHandle, minimum_degrees: f64) -> ViewHandle {
348 unsafe {
349 let g_meta = abi::metadata0(resolve::rotate_gesture_ma());
350 let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
351 let radians = minimum_degrees * std::f64::consts::PI / 180.0;
352 core::arch::asm!("blr {f}", f = in(reg) resolve::rotate_gesture_init(),
353 in("d0") radians,
354 in("x8") gesture.as_mut_ptr(),
355 in("x20") g_meta,
356 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
357 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
358 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
359 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
360 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
361 );
362 attach_gesture(view, &mut gesture, g_meta, &ROTATE_T)
363 }
364}
365
366
367
368pub(crate) type ChangeHandler = Box<dyn Fn(i64, i64)>;
370
371pub(crate) unsafe extern "C" fn invoke_change(old: *const u8, new: *const u8, payload: *mut c_void) {
372 let handler = unsafe { &*(payload as *const ChangeHandler) };
373 unsafe { handler(*(old as *const i64), *(new as *const i64)) };
374}
375
376pub fn on_change(
381 view: &ViewHandle,
382 value: i64,
383 handler: impl Fn(i64, i64) + 'static,
384) -> ViewHandle {
385 unsafe {
386 let mut v = value;
387 on_change_of(
388 view,
389 (&raw mut v).cast(),
390 resolve::int_meta(),
391 &ON_CHANGE_INNER_T,
392 invoke_change,
393 boxed_ctx(Box::new(handler) as Box<dyn Fn(i64, i64)>),
394 )
395 }
396}
397
398pub fn on_change_str(
406 view: &ViewHandle,
407 value: &str,
408 handler: impl Fn(String, String) + 'static,
409) -> ViewHandle {
410 unsafe {
411 let mut v = abi::swift_string(value);
414 on_change_of(
415 view,
416 v.as_mut_ptr(),
417 resolve::string_meta(),
418 &ON_CHANGE_STR_T,
419 invoke_change_str,
420 boxed_ctx(Box::new(handler) as Box<dyn Fn(String, String)>),
421 )
422 }
423}
424
425pub fn on_change_bool(
427 view: &ViewHandle,
428 value: bool,
429 handler: impl Fn(bool, bool) + 'static,
430) -> ViewHandle {
431 unsafe {
432 let mut v = u8::from(value);
433 on_change_of(
434 view,
435 &raw mut v,
436 resolve::bool_meta(),
437 &ON_CHANGE_BOOL_T,
438 invoke_change_bool,
439 boxed_ctx(Box::new(handler) as Box<dyn Fn(bool, bool)>),
440 )
441 }
442}
443
444pub fn on_change_f64(
446 view: &ViewHandle,
447 value: f64,
448 handler: impl Fn(f64, f64) + 'static,
449) -> ViewHandle {
450 unsafe {
451 let mut v = value;
452 on_change_of(
453 view,
454 (&raw mut v).cast(),
455 resolve::double_meta(),
456 &ON_CHANGE_F64_T,
457 invoke_change_f64,
458 boxed_ctx(Box::new(handler) as Box<dyn Fn(f64, f64)>),
459 )
460 }
461}
462
463pub(crate) unsafe extern "C" fn invoke_change_str(
464 old: *const u8,
465 new: *const u8,
466 payload: *mut c_void,
467) {
468 unsafe {
469 let f = boxed_ref::<dyn Fn(String, String)>(payload);
470 let read = |p: *const u8| {
471 let mut b = [0u8; 16];
472 b.copy_from_slice(core::slice::from_raw_parts(p, 16));
473 abi::read_swift_string(b)
476 };
477 f(read(old), read(new));
478 }
479}
480
481pub(crate) unsafe extern "C" fn invoke_change_bool(
482 old: *const u8,
483 new: *const u8,
484 payload: *mut c_void,
485) {
486 unsafe {
487 let f = boxed_ref::<dyn Fn(bool, bool)>(payload);
488 f(*old & 1 != 0, *new & 1 != 0);
489 }
490}
491
492pub(crate) unsafe extern "C" fn invoke_change_f64(
493 old: *const u8,
494 new: *const u8,
495 payload: *mut c_void,
496) {
497 unsafe {
498 let f = boxed_ref::<dyn Fn(f64, f64)>(payload);
499 f(*(old as *const f64), *(new as *const f64));
500 }
501}
502
503unsafe fn on_change_of(
514 view: &ViewHandle,
515 value: *mut u8,
516 meta: *const c_void,
517 inner: &ModifierType,
518 body: unsafe extern "C" fn(*const u8, *const u8, *mut c_void),
519 payload: *mut c_void,
520) -> ViewHandle {
521 unsafe {
522 let ctx = Box::leak(Box::new(abi::PairClosureCtx::new(body, payload)))
523 as *mut abi::PairClosureCtx;
524
525 let eq_wt = abi::get_view_wt_for_proto(meta, resolve::equatable_proto());
526 assert!(!eq_wt.is_null(), "{}: Equatable", crate::diag::type_name(meta));
527
528 let (inner_m, _, _) = inner.applied_to(view.meta);
529 let (_, _, size) = ON_CHANGE_OUTER_T.applied_to(inner_m);
530 let mut result = vec![0u8; size];
531 let this = view.value_ptr();
532 core::arch::asm!("blr {f}", f = in(reg) resolve::on_change_fn(),
537 in("x0") value,
538 in("x1") 0u64,
539 in("x2") abi::pair_closure_thunk as *const c_void,
540 in("x3") ctx,
541 in("x4") view.meta,
542 in("x5") meta,
543 in("x6") view.wt,
544 in("x7") eq_wt,
545 in("x8") result.as_mut_ptr(),
546 in("x20") this,
547 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
548 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
549 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
550 );
551 let (nested, _, _) = inner.applied_to(view.meta);
555 let (meta, wt, _) = ON_CHANGE_OUTER_T.applied_to(nested);
556 wrap_owned(result, meta, wt)
557 }
558}
559
560
561
562pub(crate) unsafe fn popover_with_binding(
572 view: &ViewHandle,
573 content: &ViewHandle,
574 binding: &[u8],
575) -> ViewHandle {
576 unsafe {
577 let b0 = u64::from_le_bytes(binding[0..8].try_into().unwrap());
578 let b1 = u64::from_le_bytes(binding[8..16].try_into().unwrap());
579 let b2 = binding[16] as u64;
580
581 let anchor_meta = abi::metadata0(resolve::popover_anchor_ma());
585 let mut anchor = vec![0u8; abi::type_bytes(anchor_meta)];
586 let source: u64;
587 core::arch::asm!("blr {f}", f = in(reg) resolve::anchor_source_bounds(),
588 lateout("x0") source,
589 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
590 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
591 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
592 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
593 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
594 clobber_abi("C"),
595 );
596 anchor[..8].copy_from_slice(&source.to_le_bytes());
597
598 let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
599 emit_tuple,
600 Child::leak(content, Content::Erased),
605 ))) as *mut abi::ClosureCtx;
606
607 let (_, _, size) = POPOVER_T.applied_to(view.meta);
608 let mut result = vec![0u8; size];
609 let this = view.value_ptr();
610 core::arch::asm!(
617 "sub sp, sp, #32",
618 "stp {sm}, {cm}, [sp]",
619 "stp {sw}, {cw}, [sp, #16]",
620 "blr {f}",
621 "add sp, sp, #32",
622 f = in(reg) resolve::popover_core_fn(),
623 sm = in(reg) view.meta,
624 cm = in(reg) resolve::anyview_meta(),
625 sw = in(reg) view.wt,
626 cw = in(reg) resolve::anyview_wt(),
627 in("x0") b0, in("x1") b1, in("x2") b2,
628 in("x3") anchor.as_mut_ptr(),
629 in("x4") 0x0100u64,
630 in("x5") 0u64,
631 in("x6") abi::swift_closure_thunk as *const c_void,
632 in("x7") ctx,
633 in("x8") result.as_mut_ptr(),
634 in("x20") this,
635 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
636 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
637 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
638 );
639 wrap_modified(result, &POPOVER_T, view)
640 }
641}
642
643pub fn popover(view: &ViewHandle, content: &ViewHandle, presented: bool) -> ViewHandle {
647 unsafe {
648 let binding = constant_binding(
649 &[u8::from(presented)],
650 resolve::bool_meta(),
651 mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
652 );
653 popover_with_binding(view, content, &binding)
654 }
655}
656
657pub fn popover_bound(view: &ViewHandle, content: &ViewHandle, state: &BoundState<bool>) -> ViewHandle {
659 unsafe {
660 let binding = bool_binding_of(state);
661 popover_with_binding(view, content, &binding)
662 }
663}
664
665
666#[derive(Clone, Copy, Debug, PartialEq)]
692pub struct DragValue {
693 pub location: (f64, f64),
695 pub start_location: (f64, f64),
697 pub velocity: (f64, f64),
704 pub time: f64,
707}
708
709impl DragValue {
710 pub fn translation(&self) -> (f64, f64) {
713 (self.location.0 - self.start_location.0, self.location.1 - self.start_location.1)
714 }
715}
716
717fn drag_value_offsets() -> Option<&'static [usize; 4]> {
725 static CACHE: std::sync::OnceLock<Option<[usize; 4]>> = std::sync::OnceLock::new();
726 CACHE
727 .get_or_init(|| unsafe {
728 let meta = abi::metadata0(resolve::drag_value_ma());
729 let offs = abi::struct_field_offsets(meta)?;
730 if offs.len() != 5 {
732 return None;
733 }
734 Some([offs[0], offs[1], offs[2], offs[3]])
735 })
736 .as_ref()
737}
738
739pub(crate) unsafe fn read_drag_value(p: *const u8) -> DragValue {
744 let Some(&[time, location, start, velocity]) = drag_value_offsets() else {
745 return DragValue {
746 location: (0.0, 0.0),
747 start_location: (0.0, 0.0),
748 velocity: (0.0, 0.0),
749 time: 0.0,
750 };
751 };
752 unsafe {
753 let point = |at: usize| {
754 let f = p.add(at) as *const f64;
755 (*f, *f.add(1))
756 };
757 let v = DragValue {
758 time: *(p.add(time) as *const f64),
759 location: point(location),
760 start_location: point(start),
761 velocity: point(velocity),
762 };
763 debug_assert_eq!(
764 v.translation(),
765 swift_translation(p),
766 "computed translation disagrees with SwiftUI's own getter — the \
767 field offsets have moved"
768 );
769 v
770 }
771}
772
773unsafe fn swift_translation(p: *const u8) -> (f64, f64) {
785 unsafe {
786 let (w, h): (f64, f64);
787 core::arch::asm!("blr {f}", f = in(reg) resolve::drag_value_translation(),
790 in("x20") p,
791 lateout("d0") w, lateout("d1") h,
792 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
793 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
794 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
795 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
796 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
797 );
798 (w, h)
799 }
800}
801
802pub(crate) unsafe extern "C" fn invoke_drag_value(value: *const u8, payload: *mut c_void) {
804 let f = unsafe { boxed_ref::<dyn Fn(DragValue)>(payload) };
805 f(unsafe { read_drag_value(value) });
806}
807
808#[derive(Clone, Copy, Debug, PartialEq)]
825pub struct MagnifyValue {
826 pub magnification: f64,
828 pub velocity: f64,
830 pub start_anchor: (f64, f64),
832 pub start_location: (f64, f64),
834 pub time: f64,
836}
837
838#[derive(Clone, Copy, Debug, PartialEq)]
843pub struct RotateValue {
844 pub radians: f64,
847 pub velocity: f64,
849 pub start_anchor: (f64, f64),
851 pub start_location: (f64, f64),
853 pub time: f64,
855}
856
857impl RotateValue {
858 pub fn degrees(&self) -> f64 {
860 self.radians.to_degrees()
861 }
862}
863
864fn pinch_value_offsets(accessor: *const c_void) -> Option<[usize; 5]> {
870 unsafe {
871 let offs = abi::struct_field_offsets(abi::metadata0(accessor))?;
872 if offs.len() != 6 {
874 return None;
875 }
876 Some([offs[0], offs[1], offs[2], offs[3], offs[4]])
877 }
878}
879
880unsafe fn read_pinch_value(p: *const u8, accessor: *const c_void) -> [f64; 7] {
885 let Some([time, amount, velocity, anchor, start]) = pinch_value_offsets(accessor) else {
886 return [0.0; 7];
887 };
888 unsafe {
889 let at = |o: usize| *(p.add(o) as *const f64);
890 let pair = |o: usize| (at(o), at(o + 8));
891 let (ax, ay) = pair(anchor);
892 let (sx, sy) = pair(start);
893 [at(amount), at(velocity), ax, ay, sx, sy, at(time)]
894 }
895}
896
897unsafe extern "C" fn invoke_magnify_value(value: *const u8, payload: *mut c_void) {
899 let f = unsafe { boxed_ref::<dyn Fn(MagnifyValue)>(payload) };
900 let v = unsafe { read_pinch_value(value, resolve::magnify_value_ma()) };
901 f(MagnifyValue {
902 magnification: v[0],
903 velocity: v[1],
904 start_anchor: (v[2], v[3]),
905 start_location: (v[4], v[5]),
906 time: v[6],
907 });
908}
909
910unsafe extern "C" fn invoke_rotate_value(value: *const u8, payload: *mut c_void) {
912 let f = unsafe { boxed_ref::<dyn Fn(RotateValue)>(payload) };
913 let v = unsafe { read_pinch_value(value, resolve::rotate_value_ma()) };
914 f(RotateValue {
915 radians: v[0],
916 velocity: v[1],
917 start_anchor: (v[2], v[3]),
918 start_location: (v[4], v[5]),
919 time: v[6],
920 });
921}
922
923pub fn on_magnify_ended(
925 view: &ViewHandle,
926 minimum_scale_delta: f64,
927 action: impl Fn(MagnifyValue) + 'static,
928) -> ViewHandle {
929 unsafe {
930 let g_meta = abi::metadata0(resolve::magnify_gesture_ma());
931 let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
932 core::arch::asm!("blr {f}", f = in(reg) resolve::magnify_gesture_init(),
933 in("d0") minimum_scale_delta,
934 in("x8") gesture.as_mut_ptr(),
935 in("x20") g_meta,
936 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
937 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
938 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
939 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
940 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
941 );
942 gesture_ended(
943 view,
944 &mut gesture,
945 g_meta,
946 invoke_magnify_value,
947 boxed_ctx(Box::new(action) as Box<dyn Fn(MagnifyValue)>),
948 )
949 }
950}
951
952pub fn on_rotate_ended(
954 view: &ViewHandle,
955 minimum_degrees: f64,
956 action: impl Fn(RotateValue) + 'static,
957) -> ViewHandle {
958 unsafe {
959 let g_meta = abi::metadata0(resolve::rotate_gesture_ma());
960 let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
961 let radians = minimum_degrees * std::f64::consts::PI / 180.0;
965 core::arch::asm!("blr {f}", f = in(reg) resolve::rotate_gesture_init(),
966 in("d0") radians,
967 in("x8") gesture.as_mut_ptr(),
968 in("x20") g_meta,
969 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
970 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
971 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
972 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
973 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
974 );
975 gesture_ended(
976 view,
977 &mut gesture,
978 g_meta,
979 invoke_rotate_value,
980 boxed_ctx(Box::new(action) as Box<dyn Fn(RotateValue)>),
981 )
982 }
983}
984
985#[cfg(test)]
986mod tests {
987 use super::*;
988
989 #[test]
1003 fn pinch_reader_matches_the_live_layout() {
1004 for accessor in [resolve::magnify_value_ma(), resolve::rotate_value_ma()] {
1005 let offsets = pinch_value_offsets(accessor).expect("six stored fields");
1006 let [time, amount, velocity, anchor, start] = offsets;
1007
1008 let size = unsafe { abi::type_bytes(abi::metadata0(accessor)) };
1010 let mut buf = vec![0u8; size];
1011 let put = |b: &mut [u8], at: usize, v: f64| {
1012 b[at..at + 8].copy_from_slice(&v.to_le_bytes());
1013 };
1014 put(&mut buf, time, 11.0);
1015 put(&mut buf, amount, 22.0);
1016 put(&mut buf, velocity, 33.0);
1017 put(&mut buf, anchor, 44.0);
1018 put(&mut buf, anchor + 8, 55.0);
1019 put(&mut buf, start, 66.0);
1020 put(&mut buf, start + 8, 77.0);
1021
1022 let got = unsafe { read_pinch_value(buf.as_ptr(), accessor) };
1023 assert_eq!(got, [22.0, 33.0, 44.0, 55.0, 66.0, 77.0, 11.0]);
1024 }
1025 }
1026
1027 #[test]
1029 fn rotate_value_converts_to_degrees() {
1030 let v = RotateValue {
1031 radians: std::f64::consts::FRAC_PI_2,
1032 velocity: 0.0,
1033 start_anchor: (0.5, 0.5),
1034 start_location: (0.0, 0.0),
1035 time: 0.0,
1036 };
1037 assert!((v.degrees() - 90.0).abs() < 1e-9);
1038 }
1039
1040 #[test]
1042 fn drag_value_translation_is_the_difference() {
1043 let v = DragValue {
1044 location: (142.0, 40.0),
1045 start_location: (70.0, 40.0),
1046 velocity: (164.0, 0.0),
1047 time: 0.0,
1048 };
1049 assert_eq!(v.translation(), (72.0, 0.0));
1050 }
1051}