use super::{
opaque_result_type, wrap_owned, DragValue, GestureMask, ViewHandle,
};
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
pub struct GestureValue<V: GestureData = ()> {
pub(crate) value: Vec<u8>,
pub(crate) meta: *const c_void,
pub(crate) value_meta: *const c_void,
marker: core::marker::PhantomData<fn(V)>,
}
pub trait GestureData: Copy + 'static {
unsafe fn read(at: *const u8) -> Self;
}
impl GestureData for () {
unsafe fn read(_: *const u8) -> Self {}
}
impl GestureData for bool {
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 {
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> {
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
}
}
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)];
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"),
);
GestureValue { value, meta, value_meta: core::ptr::null(), marker: core::marker::PhantomData }
}
}
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"),
);
let value_meta = abi::resolve_type_by_mangled_name(b"Sb");
GestureValue { value, meta, value_meta, marker: core::marker::PhantomData }
}
}
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)];
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 }
}
}
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 }
}
}
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 }
}
}
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) });
}
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> {
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;
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"),
);
GestureValue {
value: out,
meta: ended_meta,
value_meta: self.value_meta,
marker: core::marker::PhantomData,
}
}
}
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");
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;
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> {
pub fn sequenced<W: GestureData>(self, other: GestureValue<W>) -> GestureValue<()> {
unsafe {
combine(
self,
other,
resolve::gesture_sequenced(),
resolve::sequence_gesture_desc(),
)
}
}
pub fn exclusively<W: GestureData>(self, other: GestureValue<W>) -> GestureValue<()> {
unsafe {
combine(
self,
other,
resolve::gesture_exclusively(),
resolve::exclusive_gesture_desc(),
)
}
}
pub fn simultaneously<W: GestureData>(self, other: GestureValue<W>) -> GestureValue<()> {
unsafe {
combine(
self,
other,
resolve::gesture_simultaneously(),
resolve::simultaneous_gesture_desc(),
)
}
}
}
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"),
);
GestureValue {
value: out,
meta,
value_meta: core::ptr::null(),
marker: core::marker::PhantomData,
}
}
}
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();
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)
}
}
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
}
}
pub fn gesture<V: GestureData>(
view: &ViewHandle,
g: &GestureValue<V>,
mask: GestureMask,
) -> ViewHandle {
unsafe { attach(view, g, mask, resolve::gesture_fn(), resolve::gesture_oret()) }
}
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(),
)
}
}
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(),
)
}
}