swiftui-native 0.0.1

SwiftUI from pure Rust — no Swift bridge, no shim, only swift-runtime calls
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
//! Gestures as values, rather than one modifier per gesture type.
//!
//! SwiftUI's gesture API is two halves: a `Gesture` you build and compose, and
//! `.gesture(_:including:)` that attaches one to a view. The bindings that came
//! first collapsed the two — `drag_gesture(view, distance)` builds a
//! `DragGesture` and attaches it in one call, with the mask fixed at `.all` and
//! no way to say what should happen while the drag is in flight. That covers
//! `.gesture(DragGesture())` and nothing else.
//!
//! Here the gesture is a value:
//!
//! ```no_run
//! # use swiftui_native::views::{self, GestureMask};
//! // The handler's argument follows from the gesture: a drag gives a
//! // `DragValue`, a long press a `bool`, a tap nothing.
//! let g = views::drag(0.0)
//!     .on_changed(|v| println!("moved {:?}", v.translation()))
//!     .on_ended(|_| println!("done"));
//! let v = views::gesture(&views::text("drag me"), &g, GestureMask::All);
//! ```
//!
//! `on_changed` is the half that was missing entirely, and it is the half that
//! makes a gesture useful: `onEnded` is told what happened after the fact,
//! `onChanged` is told while it is happening, which is what moving something
//! under the finger needs.

use super::{
    opaque_result_type, wrap_owned, DragValue, GestureMask, ViewHandle,
};
use crate::abi;
use crate::resolve;
use core::ffi::c_void;

/// A built `Gesture` — its bytes, its type, and the type its handlers see.
///
/// The `Value` is carried because `onChanged` needs it: that extension is
/// constrained to `Value: Equatable` and takes the witness as an argument, so
/// a gesture that does not know its own value type cannot be given a changed
/// handler. `onEnded` does not need it, which is why the first bindings could
/// get by without.
///
/// Dropping one does *not* destroy the Swift value. Every gesture this module
/// builds is a plain aggregate of scalars — `DragGesture` is a `CGFloat` and a
/// coordinate space, `TapGesture` an `Int` — and the combinators wrap those
/// without adding a reference. The closures are a different matter: they are
/// leaked on purpose, because SwiftUI stores them.
pub struct GestureValue<V: GestureData = ()> {
    pub(crate) value: Vec<u8>,
    pub(crate) meta: *const c_void,
    /// `Gesture.Value`, or null where the gesture has none worth passing on.
    pub(crate) value_meta: *const c_void,
    /// `V` is what handlers are given, not something this stores.
    marker: core::marker::PhantomData<fn(V)>,
}

/// What a gesture hands its handlers, as a Rust type.
///
/// The gesture's own type decides this — a drag reports a [`DragValue`], a
/// long press a `bool` — so a handler's argument follows from the gesture it
/// is attached to rather than being a case anyone has to match on.
///
/// Implemented per type rather than derived: reading the bytes SwiftUI passes
/// is a decision about *that* `Gesture.Value`'s layout.
pub trait GestureData: Copy + 'static {
    /// Read the value SwiftUI passed by address.
    ///
    /// # Safety
    /// `at` must point at a live `Gesture.Value` of the matching Swift type.
    unsafe fn read(at: *const u8) -> Self;
}

impl GestureData for () {
    unsafe fn read(_: *const u8) -> Self {}
}

impl GestureData for bool {
    /// `LongPressGesture.Value` — one byte, true once the press completes.
    unsafe fn read(at: *const u8) -> Self {
        unsafe { *at != 0 }
    }
}

impl GestureData for DragValue {
    unsafe fn read(at: *const u8) -> Self {
        unsafe { super::read_drag_value(at) }
    }
}

impl GestureData for f64 {
    /// `MagnifyGesture.Value` and `RotateGesture.Value` both lead with the
    /// scalar the gesture is about — a magnification, or an angle in radians.
    unsafe fn read(at: *const u8) -> Self {
        unsafe {
            f64::from_le_bytes(
                core::slice::from_raw_parts(at, 8).try_into().expect("8 bytes"),
            )
        }
    }
}

impl<V: GestureData> GestureValue<V> {
    /// The `Gesture` witness for this gesture's type.
    fn wt(&self) -> *const c_void {
        let wt = unsafe { abi::get_view_wt_for_proto(self.meta, resolve::gesture_proto()) };
        assert!(!wt.is_null(), "{}: Gesture", crate::diag::type_name(self.meta));
        wt
    }
}

/// `TapGesture(count:)`.
pub fn tap(count: i64) -> GestureValue<()> {
    unsafe {
        let meta = abi::metadata0(resolve::tap_gesture_ma());
        assert!(!meta.is_null(), "TapGesture metadata");
        let mut value = vec![0u8; abi::type_bytes(meta)];
        // `TapGesture.init(count:)` is `(sret, i64)` — no metatype, unlike the
        // gestures next door whose initialisers take one in x20. Read from the
        // IR rather than copied from its neighbour.
        core::arch::asm!("blr {f}", f = in(reg) resolve::tap_gesture_init(),
            in("x0") count,
            in("x8") value.as_mut_ptr(),
            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("x20") _, lateout("x21") _,
            clobber_abi("C"),
        );
        // `TapGesture.Value` is `Void`, which has no accessor and nothing for
        // a handler to read.
        GestureValue { value, meta, value_meta: core::ptr::null(), marker: core::marker::PhantomData }
    }
}

/// `LongPressGesture(minimumDuration:maximumDistance:)`.
pub fn long_press(duration: f64, max_distance: f64) -> GestureValue<bool> {
    unsafe {
        let meta = abi::metadata0(resolve::long_press_ma());
        assert!(!meta.is_null(), "LongPressGesture metadata");
        let mut value = vec![0u8; abi::type_bytes(meta)];
        core::arch::asm!("blr {f}", f = in(reg) resolve::long_press_init(),
            in("d0") duration, in("d1") max_distance,
            in("x8") value.as_mut_ptr(),
            in("x20") 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"),
        );
        // `LongPressGesture.Value` is `Bool` — a stdlib type, so its metadata
        // is the one the runtime already has rather than a `…Ma` call.
        let value_meta = abi::resolve_type_by_mangled_name(b"Sb");
        GestureValue { value, meta, value_meta, marker: core::marker::PhantomData }
    }
}

/// `DragGesture(minimumDistance:coordinateSpace: .local)`.
pub fn drag(minimum_distance: f64) -> GestureValue<DragValue> {
    unsafe {
        let meta = abi::metadata0(resolve::drag_gesture_ma());
        assert!(!meta.is_null(), "DragGesture metadata");
        let space_meta = abi::metadata0(resolve::local_space_ma());
        let mut value = vec![0u8; abi::type_bytes(meta)];
        // `init<C: CoordinateSpaceProtocol>(minimumDistance:coordinateSpace:)`:
        // d0 = distance, x0 = @in C (`LocalCoordinateSpace` is zero-size),
        // x1 = C.Type, x2 = C: CoordinateSpaceProtocol WT, x8 = @out.
        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") value.as_mut_ptr(),
            in("x20") 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"),
        );
        let value_meta = abi::metadata0(resolve::drag_value_ma());
        GestureValue { value, meta, value_meta, marker: core::marker::PhantomData }
    }
}

/// `MagnifyGesture(minimumScaleDelta:)`.
pub fn magnify(minimum_scale_delta: f64) -> GestureValue<f64> {
    unsafe {
        let meta = abi::metadata0(resolve::magnify_gesture_ma());
        assert!(!meta.is_null(), "MagnifyGesture metadata");
        let mut value = vec![0u8; abi::type_bytes(meta)];
        core::arch::asm!("blr {f}", f = in(reg) resolve::magnify_gesture_init(),
            in("d0") minimum_scale_delta,
            in("x8") value.as_mut_ptr(),
            in("x20") 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"),
        );
        let value_meta = abi::metadata0(resolve::magnify_value_ma());
        GestureValue { value, meta, value_meta, marker: core::marker::PhantomData }
    }
}

/// `RotateGesture(minimumAngleDelta:)`, taking degrees.
///
/// SwiftUI's parameter is an `Angle`, which stores radians; degrees are
/// converted here so that the call site reads the way the documentation does.
pub fn rotate(minimum_degrees: f64) -> GestureValue<f64> {
    unsafe {
        let meta = abi::metadata0(resolve::rotate_gesture_ma());
        assert!(!meta.is_null(), "RotateGesture metadata");
        let mut value = vec![0u8; abi::type_bytes(meta)];
        let radians = minimum_degrees * core::f64::consts::PI / 180.0;
        core::arch::asm!("blr {f}", f = in(reg) resolve::rotate_gesture_init(),
            in("d0") radians,
            in("x8") value.as_mut_ptr(),
            in("x20") 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"),
        );
        let value_meta = abi::metadata0(resolve::rotate_value_ma());
        GestureValue { value, meta, value_meta, marker: core::marker::PhantomData }
    }
}

/// The `(Value) -> ()` body SwiftUI calls, for one value type.
///
/// Monomorphised rather than switching on a stored tag: the value's type is
/// known where the handler is installed, so reading it is a direct call and
/// not a match nobody can get wrong twice.
///
/// # Safety
/// `payload` must be a leaked `Box<Box<dyn Fn(V)>>` and `value` a pointer to a
/// live `V` as Swift lays it out.
unsafe extern "C" fn invoke_handler<V: GestureData>(value: *const u8, payload: *mut c_void) {
    let f = unsafe { &*(payload as *const Box<dyn Fn(V)>) };
    f(unsafe { V::read(value) });
}

/// Leak a handler for SwiftUI to hold. It outlives the call by design.
fn leak_handler<V: GestureData>(action: impl Fn(V) + 'static) -> *mut c_void {
    let boxed: Box<dyn Fn(V)> = Box::new(action);
    Box::into_raw(Box::new(boxed)) as *mut c_void
}

impl<V: GestureData> GestureValue<V> {
    /// `Gesture.onEnded(_:)` — run `action` when the gesture completes.
    ///
    /// Returns `_EndedGesture<Self>`, which is itself a `Gesture`, so these
    /// chain the way they do in Swift.
    pub fn on_ended(self, action: impl Fn(V) + 'static) -> GestureValue<V> {
        unsafe {
            let g_wt = self.wt();
            let ended_meta = abi::generic_metadata2(
                resolve::ended_gesture_ma(),
                self.meta,
                g_wt,
            );
            assert!(!ended_meta.is_null(), "_EndedGesture metadata");
            let mut out = vec![0u8; abi::type_bytes(ended_meta)];

            let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
                invoke_handler::<V>,
                leak_handler(action),
            ))) as *mut abi::ValueClosureCtx;

            let mut this = self.value;
            // x0/x1 = closure, x2 = Self.Type, x3 = Self: Gesture WT,
            // x8 = @out, x20 = @in_guaranteed self.
            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") self.meta,
                in("x3") g_wt,
                in("x8") out.as_mut_ptr(),
                in("x20") this.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"),
            );
            // `_EndedGesture<G>.Value` is `G.Value`: the wrapper passes the
            // value through untouched, so the shape carries over.
            GestureValue {
                value: out,
                meta: ended_meta,
                value_meta: self.value_meta,
                marker: core::marker::PhantomData,
            }
        }
    }

    /// `Gesture.onChanged(_:)` — run `action` every time the value moves.
    ///
    /// This is a *different* extension from [`GestureValue::on_ended`],
    /// constrained to `Value: Equatable`, so it takes that witness as a sixth
    /// argument and returns `_ChangedGesture`. A gesture whose value has no
    /// metadata here — a tap, whose value is `Void` — cannot have one, and
    /// says so rather than building something that would not fire.
    pub fn on_changed(self, action: impl Fn(V) + 'static) -> GestureValue<V> {
        assert!(
            !self.value_meta.is_null(),
            "onChanged needs an Equatable Value; TapGesture's is Void — use on_ended"
        );
        unsafe {
            let g_wt = self.wt();
            let eq_wt = abi::get_view_wt_for_proto(self.value_meta, resolve::equatable_proto());
            assert!(!eq_wt.is_null(), "gesture Value: Equatable");

            // `_ChangedGesture<Content>` exports no metadata accessor, only its
            // descriptor. The argument vector is one word per parameter then
            // one per conformance requirement, in declaration order:
            // Content, Content: Gesture, Content.Value: Equatable.
            let changed_meta = abi::generic_metadata_from_descriptor(
                resolve::changed_gesture_desc(),
                &[self.meta, g_wt, eq_wt],
            );
            assert!(!changed_meta.is_null(), "_ChangedGesture metadata");
            let mut out = vec![0u8; abi::type_bytes(changed_meta)];

            let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
                invoke_handler::<V>,
                leak_handler(action),
            ))) as *mut abi::ValueClosureCtx;

            let mut this = self.value;
            // x0/x1 = closure, x2 = Self.Type, x3 = Self: Gesture WT,
            // x4 = Self.Value: Equatable WT, x8 = @out, x20 = self.
            core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_on_changed(),
                in("x0") abi::value_closure_thunk as *const () as u64,
                in("x1") ctx as u64,
                in("x2") self.meta,
                in("x3") g_wt,
                in("x4") eq_wt,
                in("x8") out.as_mut_ptr(),
                in("x20") this.as_mut_ptr(),
                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"),
            );
            GestureValue {
                value: out,
                meta: changed_meta,
                value_meta: self.value_meta,
                marker: core::marker::PhantomData,
            }
        }
    }
}

impl<V: GestureData> GestureValue<V> {
    /// `Gesture.sequenced(before:)` — this one, then the other.
    ///
    /// A long press that has to complete before a drag begins is the shape
    /// this exists for: `long_press(0.5, 10.0).sequenced(drag(0.0))`.
    pub fn sequenced<W: GestureData>(self, other: GestureValue<W>) -> GestureValue<()> {
        unsafe {
            combine(
                self,
                other,
                resolve::gesture_sequenced(),
                resolve::sequence_gesture_desc(),
            )
        }
    }

    /// `Gesture.exclusively(before:)` — whichever recognises first wins.
    pub fn exclusively<W: GestureData>(self, other: GestureValue<W>) -> GestureValue<()> {
        unsafe {
            combine(
                self,
                other,
                resolve::gesture_exclusively(),
                resolve::exclusive_gesture_desc(),
            )
        }
    }

    /// `Gesture.simultaneously(with:)` — both may recognise at once.
    pub fn simultaneously<W: GestureData>(self, other: GestureValue<W>) -> GestureValue<()> {
        unsafe {
            combine(
                self,
                other,
                resolve::gesture_simultaneously(),
                resolve::simultaneous_gesture_desc(),
            )
        }
    }
}

/// The body of all three combinators, which differ only in the symbol called
/// and the wrapper they build.
///
/// `(x0 = @in other, x1 = Self.Type, x2 = Other.Type, x3 = Self: Gesture WT,
/// x4 = Other: Gesture WT, x8 = @out, x20 = self)` — read from the IR, where
/// the two metadata arguments arrive in *declaration* order (the receiver
/// first), not in the order the call site writes them.
///
/// # Safety
/// `f` and `desc` must be a matching combinator and its result descriptor.
unsafe fn combine<A: GestureData, B: GestureData>(
    first: GestureValue<A>,
    second: GestureValue<B>,
    f: *const c_void,
    desc: *const c_void,
) -> GestureValue<()> {
    unsafe {
        let a_wt = first.wt();
        let b_wt = second.wt();
        let meta = abi::generic_metadata_from_descriptor(
            desc,
            &[first.meta, second.meta, a_wt, b_wt],
        );
        assert!(!meta.is_null(), "combined gesture metadata");
        let mut out = vec![0u8; abi::type_bytes(meta)];
        let mut lhs = first.value;
        let mut rhs = second.value;
        core::arch::asm!("blr {f}", f = in(reg) f,
            in("x0") rhs.as_mut_ptr(),
            in("x1") first.meta,
            in("x2") second.meta,
            in("x3") a_wt,
            in("x4") b_wt,
            in("x8") out.as_mut_ptr(),
            in("x20") lhs.as_mut_ptr(),
            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"),
        );
        // A combined gesture's `Value` is its own type — an enum for
        // `SequenceGesture` and `ExclusiveGesture`, a pair of optionals for
        // `SimultaneousGesture` — and none of them has a Rust counterpart
        // here. So a handler on one still fires and is told nothing, and
        // `on_changed` refuses rather than reading bytes of a shape it does
        // not know.
        GestureValue {
            value: out,
            meta,
            value_meta: core::ptr::null(),
            marker: core::marker::PhantomData,
        }
    }
}

/// Attach a gesture through one of the three entry points.
///
/// `.gesture`, `.highPriorityGesture` and `.simultaneousGesture` take exactly
/// the same arguments and differ only in how they compete with the gestures
/// already on the view, so one body serves all three.
///
/// # Safety
/// `f` and `oret` must be a matching modifier and opaque-result descriptor.
unsafe fn attach<V: GestureData>(
    view: &ViewHandle,
    g: &GestureValue<V>,
    mask: GestureMask,
    f: *const c_void,
    oret: *const c_void,
) -> ViewHandle {
    unsafe {
        let g_wt = g.wt();
        let subst = [view.meta, g.meta, view.wt, g_wt];
        let (meta, wt, size) = opaque_result_type(oret, &subst);
        let mut result = vec![0u8; size];
        let this = view.value_ptr();
        // `GestureMask` is a frozen `OptionSet` over `UInt32`, so it travels
        // as one register-sized raw value rather than by address.
        let raw = mask_raw(mask);
        let mut value = g.value.clone();
        core::arch::asm!("blr {f}", f = in(reg) f,
            in("x0") value.as_mut_ptr(),
            in("x1") raw,
            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_owned(result, meta, wt)
    }
}

/// The `UInt32` behind a `GestureMask` case, read from SwiftUI rather than
/// assumed: `.all` has been spelled `3` by hand throughout this crate, which
/// happens to be right and is not something the source says.
fn mask_raw(mask: GestureMask) -> u64 {
    unsafe {
        let meta = abi::metadata0(resolve::gesture_mask_ma());
        assert!(!meta.is_null(), "GestureMask metadata");
        super::static_getter(mask.resolved(), meta).0
    }
}

/// `.gesture(_:including:)`.
pub fn gesture<V: GestureData>(
    view: &ViewHandle,
    g: &GestureValue<V>,
    mask: GestureMask,
) -> ViewHandle {
    unsafe { attach(view, g, mask, resolve::gesture_fn(), resolve::gesture_oret()) }
}

/// `.highPriorityGesture(_:including:)` — this gesture wins over the view's own.
pub fn high_priority_gesture_of<V: GestureData>(
    view: &ViewHandle,
    g: &GestureValue<V>,
    mask: GestureMask,
) -> ViewHandle {
    unsafe {
        attach(
            view,
            g,
            mask,
            resolve::high_priority_gesture_fn(),
            resolve::high_priority_gesture_oret(),
        )
    }
}

/// `.simultaneousGesture(_:including:)` — both this and the view's own run.
pub fn simultaneous_gesture_of<V: GestureData>(
    view: &ViewHandle,
    g: &GestureValue<V>,
    mask: GestureMask,
) -> ViewHandle {
    unsafe {
        attach(
            view,
            g,
            mask,
            resolve::simultaneous_gesture_fn(),
            resolve::simultaneous_gesture_oret(),
        )
    }
}