use super::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::swift_type;
swift_cases! {
pub enum LabeledPairRole {
Label = 0,
Content = 1,
}
}
pub fn accessibility_labeled_pair(
view: &ViewHandle,
role: LabeledPairRole,
id: HashableValue<'_>,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::a11y_labeled_pair_role_ma());
let r = abi::enum_argument(meta, role.tag());
let (bytes, id_meta, wt) = id.parts();
generic1_modifier_ints(
resolve::a11y_labeled_pair_fn(),
resolve::a11y_labeled_pair_oret(),
view,
id_meta,
wt,
&[r, bytes.as_ptr() as u64, namespace_id()],
[0.0; 4],
)
}
}
pub fn glass_effect_id(view: &ViewHandle, id: Option<HashableValue<'_>>) -> ViewHandle {
unsafe {
let probe = id.unwrap_or(HashableValue::Int(0));
let (bytes, meta, wt) = probe.parts();
let opt = abi::generic_metadata1(resolve::optional_ma(), meta);
let mut buf = vec![0u8; abi::type_bytes(opt)];
if id.is_some() {
buf[..bytes.len()].copy_from_slice(&bytes);
}
abi::store_optional_tag(&mut buf, meta, id.is_some());
generic1_modifier_ints(
resolve::glass_effect_id_fn(),
resolve::glass_effect_id_oret(),
view,
meta,
wt,
&[buf.as_ptr() as u64, namespace_id()],
[0.0; 4],
)
}
}
pub(crate) unsafe fn image_word(name: &str) -> u64 {
unsafe {
let s = abi::swift_string(name);
abi::call_regs(
resolve::image_system_init(),
abi::ints(&[
u64::from_le_bytes(s[..8].try_into().expect("lo")),
u64::from_le_bytes(s[8..].try_into().expect("hi")),
]),
[0.0; 4],
resolve::image_meta() as u64,
)[0]
}
}
pub(crate) unsafe fn image_named_word(name: &str) -> u64 {
unsafe {
let s = abi::swift_string(name);
abi::call_regs(
resolve::image_named_init(),
abi::ints(&[
u64::from_le_bytes(s[..8].try_into().expect("lo")),
u64::from_le_bytes(s[8..].try_into().expect("hi")),
0, ]),
[0.0; 4],
resolve::image_meta() as u64,
)[0]
}
}
pub fn image_named(name: &str) -> ViewHandle {
unsafe {
let word = image_named_word(name);
ViewHandle::wrap(
&word as *const u64 as *const c_void,
resolve::image_meta(),
resolve::image_wt(),
)
}
}
pub fn image_system(name: &str) -> ViewHandle {
unsafe {
let word = image_word(name);
ViewHandle::wrap(
&word as *const u64 as *const c_void,
resolve::image_meta(),
resolve::image_wt(),
)
}
}
pub(crate) unsafe fn image_bytes(name: &str) -> Vec<u8> {
unsafe { image_word(name).to_le_bytes().to_vec() }
}
pub fn dialog_icon(view: &ViewHandle, system_name: Option<&str>) -> ViewHandle {
unsafe {
let word = system_name.map_or(0, |n| {
u64::from_le_bytes(image_bytes(n)[..8].try_into().expect("image word"))
});
opaque_modifier(
resolve::dialog_icon_fn(),
resolve::dialog_icon_oret(),
view,
&[word],
[0.0; 4],
)
}
}
pub fn default_app_storage_standard(view: &ViewHandle) -> ViewHandle {
unsafe {
let cls = crate::objc::class_named(c"NSUserDefaults");
type Send0 = unsafe extern "C" fn(*mut c_void, *const c_void) -> *mut c_void;
let send: Send0 = core::mem::transmute(crate::objc::msg_send_addr());
let defaults = send(cls, crate::objc::sel(c"standardUserDefaults"));
assert!(!defaults.is_null(), "UserDefaults.standard");
opaque_modifier(
resolve::default_app_storage_fn(),
resolve::default_app_storage_oret(),
view,
&[defaults as u64],
[0.0; 4],
)
}
}
pub fn default_app_storage_suite(view: &ViewHandle, suite: &str) -> Option<ViewHandle> {
unsafe {
let cls = crate::objc::class_named(c"NSUserDefaults");
type Alloc = unsafe extern "C" fn(*mut c_void, *const c_void) -> *mut c_void;
type InitSuite =
unsafe extern "C" fn(*mut c_void, *const c_void, *mut c_void) -> *mut c_void;
let send_alloc: Alloc = core::mem::transmute(crate::objc::msg_send_addr());
let send_init: InitSuite = core::mem::transmute(crate::objc::msg_send_addr());
let raw = send_alloc(cls, crate::objc::sel(c"alloc"));
let name = crate::objc::nsstring(suite);
let defaults = send_init(raw, crate::objc::sel(c"initWithSuiteName:"), name);
if defaults.is_null() {
return None;
}
Some(opaque_modifier(
resolve::default_app_storage_fn(),
resolve::default_app_storage_oret(),
view,
&[defaults as u64],
[0.0; 4],
))
}
}
pub(crate) unsafe fn file_url(path: &str) -> Vec<u8> {
unsafe {
let ns_string = crate::objc::class_named(c"NSString");
type Send1<T> = unsafe extern "C" fn(*mut c_void, *const c_void, T) -> *mut c_void;
let send: Send1<*const i8> = core::mem::transmute(crate::objc::msg_send_addr());
let cpath = std::ffi::CString::new(path).expect("path");
let s = send(ns_string, crate::objc::sel(c"stringWithUTF8String:"), cpath.as_ptr());
let ns_url = crate::objc::class_named(c"NSURL");
let send_obj: Send1<*mut c_void> = core::mem::transmute(crate::objc::msg_send_addr());
let url = send_obj(ns_url, crate::objc::sel(c"fileURLWithPath:"), s);
assert!(!url.is_null(), "NSURL for {path}");
let meta = crate::type_by_name!(swift_type!(Foundation.URL).as_bytes());
let mut out = vec![0u8; abi::type_bytes(meta)];
abi::call_out(
resolve::url_bridge_from_objc(),
abi::ints(&[url as u64]),
[0.0; 4],
meta as u64,
out.as_mut_ptr(),
);
out
}
}
pub fn file_dialog_default_directory(view: &ViewHandle, path: Option<&str>) -> ViewHandle {
unsafe {
let payload = crate::type_by_name!(swift_type!(Foundation.URL).as_bytes());
let v = path.map(|p| file_url(p));
let b = opt_resilient_bytes(payload, swift_type!(opt Foundation.URL).as_bytes(), v.as_deref());
modifier_indirect(
resolve::file_dialog_default_directory_fn(),
resolve::file_dialog_default_directory_oret(),
view,
&b,
)
}
}
pub fn preview_device(view: &ViewHandle, name: Option<&str>) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::preview_device_ma());
let mut buf = vec![0u8; abi::type_bytes(meta)];
if let Some(n) = name {
let s = abi::swift_string(n);
abi::call_out(
resolve::preview_device_init(),
abi::ints(&[
u64::from_le_bytes(s[..8].try_into().expect("lo")),
u64::from_le_bytes(s[8..].try_into().expect("hi")),
]),
[0.0; 4],
meta as u64,
buf.as_mut_ptr(),
);
}
let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.PreviewDevice).as_bytes(), name.map(|_| &buf[..]));
modifier_indirect(resolve::preview_device_fn(), resolve::preview_device_oret(), view, &b)
}
}
#[cfg(test)]
mod url_tests {
use super::*;
#[test]
fn bridged_file_url_round_trips() {
unsafe {
let meta = crate::type_by_name!(swift_type!(Foundation.URL).as_bytes());
let mut bytes = file_url("/tmp/rswift-parity");
let d = crate::diag::dump_any(bytes.as_mut_ptr().cast(), meta, 1);
assert!(d.contains("file:///tmp/rswift-parity"), "bridged URL was {d}");
}
}
#[test]
fn file_dialog_default_directory_builds_either_way() {
let _ = file_dialog_default_directory(&text("Fx"), Some("/tmp/rswift-parity"));
let _ = file_dialog_default_directory(&text("Fx"), None);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TextContentKind {
Username,
Password,
OneTimeCode,
}
impl TextContentKind {
fn constant(self) -> &'static core::ffi::CStr {
match self {
Self::Username => c"NSTextContentTypeUsername",
Self::Password => c"NSTextContentTypePassword",
Self::OneTimeCode => c"NSTextContentTypeOneTimeCode",
}
}
}
pub fn text_content_type(view: &ViewHandle, kind: Option<TextContentKind>) -> ViewHandle {
unsafe {
let word = kind.map_or(0, |k| {
let slot = resolve::sym(k.constant());
assert!(!slot.is_null(), "missing constant: {:?}", k.constant());
*(slot as *const u64)
});
opaque_modifier(
resolve::text_content_type_fn(),
resolve::text_content_type_oret(),
view,
&[word],
[0.0; 4],
)
}
}
#[derive(Clone, Copy)]
pub struct Image(u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResizingMode {
Tile,
Stretch,
}
impl ResizingMode {
fn descriptor(self) -> &'static core::ffi::CStr {
match self {
Self::Tile => c"$s7SwiftUI5ImageV12ResizingModeO4tileyA2EmFWC",
Self::Stretch => c"$s7SwiftUI5ImageV12ResizingModeO7stretchyA2EmFWC",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Interpolation {
None,
Low,
Medium,
High,
}
impl Interpolation {
fn descriptor(self) -> &'static core::ffi::CStr {
match self {
Self::None => c"$s7SwiftUI5ImageV13InterpolationO4noneyA2EmFWC",
Self::Low => c"$s7SwiftUI5ImageV13InterpolationO3lowyA2EmFWC",
Self::Medium => c"$s7SwiftUI5ImageV13InterpolationO6mediumyA2EmFWC",
Self::High => c"$s7SwiftUI5ImageV13InterpolationO4highyA2EmFWC",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TemplateRenderingMode {
Template,
Original,
}
impl TemplateRenderingMode {
fn descriptor(self) -> &'static core::ffi::CStr {
match self {
Self::Template => c"$s7SwiftUI5ImageV21TemplateRenderingModeO8templateyA2EmFWC",
Self::Original => c"$s7SwiftUI5ImageV21TemplateRenderingModeO8originalyA2EmFWC",
}
}
}
impl Image {
pub fn system(name: &str) -> Self {
Self(unsafe { image_word(name) })
}
pub fn system_variable(name: &str, value: Option<f64>) -> Self {
unsafe {
let s = abi::swift_string(name);
let (bits, tag) = match value {
Some(v) => (v.to_bits(), 0u64),
None => (0, 1),
};
Self(
abi::call_regs(
resolve::image_sysname_variable_init(),
abi::ints(&[
u64::from_le_bytes(s[..8].try_into().expect("lo")),
u64::from_le_bytes(s[8..].try_into().expect("hi")),
bits,
tag,
]),
[0.0; 4],
resolve::image_meta() as u64,
)[0],
)
}
}
pub fn allowed_dynamic_range_high(self) -> Self {
unsafe {
let inner = abi::metadata0(resolve::image_dynamic_range_ma());
assert!(!inner.is_null(), "Image.DynamicRange metadata");
let value = super::static_getter_indirect(
resolve::image_dynamic_range_high(),
inner,
abi::type_bytes(inner),
);
let mut opt = vec![0u8; abi::type_bytes(inner) + 8];
opt[..value.len()].copy_from_slice(&value);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::image_dynamic_range_fn(),
in("x0") opt.as_mut_ptr(),
in("x1") self.0,
lateout("x0") out,
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"),
);
Self(out)
}
}
pub fn named(name: &str) -> Self {
Self(unsafe { image_named_word(name) })
}
pub fn resizable(self, mode: ResizingMode) -> Self {
unsafe {
let mut buf = resilient_case(
RESIZING_MODE,
mode.descriptor(),
);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::image_resizable_fn(),
in("d0") 0.0f64, in("d1") 0.0f64, in("d2") 0.0f64, in("d3") 0.0f64,
in("x0") buf.as_mut_ptr(),
in("x1") self.0,
lateout("x0") out,
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"),
);
Self(out)
}
}
pub fn interpolation(self, mode: Interpolation) -> Self {
unsafe {
let mut buf = resilient_case(INTERPOLATION, mode.descriptor());
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::image_interpolation_fn(),
in("x0") buf.as_mut_ptr(),
in("x1") self.0,
lateout("x0") out,
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"),
);
Self(out)
}
}
pub fn symbol_rendering_mode(self, mode: Option<super::SymbolRenderingMode>) -> Self {
unsafe {
let meta = abi::metadata0(resolve::symbol_rendering_mode_ma());
let v = mode.map(|m| super::resilient_static(meta, m.getter()));
let mut buf = super::opt_resilient_bytes(
meta,
swift_type!(opt SwiftUI.SymbolRenderingMode).as_bytes(),
v.as_deref(),
);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::image_symbol_rendering_mode_fn(),
in("x0") buf.as_mut_ptr(),
in("x1") self.0,
lateout("x0") out,
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"),
);
Self(out)
}
}
pub fn antialiased(self, on: bool) -> Self {
unsafe {
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::image_antialiased_fn(),
in("x0") u64::from(on),
in("x1") self.0,
lateout("x0") out,
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"),
);
Self(out)
}
}
pub fn rendering_mode(self, mode: Option<TemplateRenderingMode>) -> Self {
unsafe {
let mangled = TEMPLATE_RENDERING_MODE;
let mut buf = match mode {
Some(m) => resilient_case(mangled, m.descriptor()),
None => {
let mut opt = mangled.to_vec();
opt.extend_from_slice(b"Sg");
let meta = abi::resolve_type_by_mangled_name(&opt);
assert!(!meta.is_null(), "Optional<TemplateRenderingMode>");
let mut buf = vec![0u8; abi::type_bytes(meta).max(1)];
abi::inject_enum_tag(&mut buf, meta, 1);
buf
}
};
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::image_rendering_mode_fn(),
in("x0") buf.as_mut_ptr(),
in("x1") self.0,
lateout("x0") out,
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"),
);
Self(out)
}
}
pub fn view(self) -> ViewHandle {
unsafe {
ViewHandle::wrap(
&self.0 as *const u64 as *const c_void,
resolve::image_meta(),
resolve::image_wt(),
)
}
}
}
const RESIZING_MODE: &[u8] = b"7SwiftUI5ImageV12ResizingModeO";
const INTERPOLATION: &[u8] = b"7SwiftUI5ImageV13InterpolationO";
const TEMPLATE_RENDERING_MODE: &[u8] = b"7SwiftUI5ImageV21TemplateRenderingModeO";
unsafe fn resilient_case(mangled: &[u8], case_descriptor: &'static core::ffi::CStr) -> Vec<u8> {
unsafe {
let meta = abi::resolve_type_by_mangled_name(mangled);
assert!(!meta.is_null(), "{}", crate::diag::demangle_type(mangled));
let tag = resolve::sym(case_descriptor) as *const u32;
assert!(!tag.is_null(), "enum case descriptor: {case_descriptor:?}");
let mut buf = vec![0u8; abi::type_bytes(meta).max(1)];
abi::inject_enum_tag(&mut buf, meta, tag.read());
buf
}
}
pub fn async_image(spec: Option<&str>, scale: f64) -> ViewHandle {
unsafe {
let ty = crate::mangle::ty("AsyncImage").of(vec![crate::mangle::ty("Image")]).mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "AsyncImage<Image> metadata");
let mut url = super::optional_url(spec);
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::async_image_init(),
in("x0") url.as_mut_ptr(),
in("d0") scale,
in("x8") value.as_mut_ptr(),
in("x20") meta,
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 wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "AsyncImage !: View");
wrap_buf(&value, meta, wt)
}
}
pub fn share_link_url(spec: &str) -> ViewHandle {
unsafe {
let ty = crate::mangle::ty("ShareLink")
.of(vec![
crate::mangle::Type::Verbatim("s15CollectionOfOneVy10Foundation3URLVG".into()),
crate::mangle::Type::Verbatim("s5NeverO".into()),
crate::mangle::Type::Verbatim("s5NeverO".into()),
crate::mangle::ty("DefaultShareLinkLabel"),
])
.mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "ShareLink metadata: {ty}");
let mut url = super::optional_url(Some(spec));
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!(
"str xzr, [sp, #-16]!",
"blr {f}",
"add sp, sp, #16",
f = in(reg) resolve::share_link_url_init(),
in("x0") url.as_mut_ptr(),
in("x1") 0u64, in("x2") 0u64, in("x3") 0u64, in("x4") 0u64,
in("x5") 0u64, in("x6") 0u64, in("x7") 0u64,
in("x8") value.as_mut_ptr(),
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"),
);
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "ShareLink !: View");
wrap_buf(&value, meta, wt)
}
}