1use crate::abi;
43#[cfg(target_os = "macos")]
45use crate::mangle::{self, swift_type};
46use crate::resolve;
47use crate::view::IntoView;
48use crate::views::{self, ViewHandle};
49use core::ffi::c_void;
50
51struct Value {
60 bytes: Vec<u8>,
61 meta: *const c_void,
62 wt: *const c_void,
63}
64
65impl Value {
66 unsafe fn wrap(bytes: Vec<u8>, meta: *const c_void, wt: *const c_void) -> Self {
72 debug_assert!(!meta.is_null() && !wt.is_null(), "a value needs a type and a witness");
73 Self { bytes, meta, wt }
74 }
75
76 fn ptr(&self) -> *const c_void {
77 self.bytes.as_ptr() as *const c_void
78 }
79
80 unsafe fn move_into(self, out: *mut u8) {
89 let me = core::mem::ManuallyDrop::new(self);
90 unsafe { core::ptr::copy_nonoverlapping(me.bytes.as_ptr(), out, me.bytes.len()) };
91 }
92}
93
94impl Drop for Value {
95 fn drop(&mut self) {
96 unsafe {
97 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
98 if vwt.size > 0 {
99 (vwt.destroy)(self.bytes.as_mut_ptr() as *mut c_void, self.meta);
100 }
101 }
102 }
103}
104
105pub struct SceneHandle(Value);
108
109pub struct CommandsHandle(Value);
111
112impl SceneHandle {
113 pub fn type_name(&self) -> String {
115 crate::diag::type_name(self.0.meta)
116 }
117
118 pub(crate) unsafe fn move_into(self, out: *mut u8) {
123 unsafe { self.0.move_into(out) }
124 }
125
126 pub(crate) fn meta(&self) -> *const c_void {
127 self.0.meta
128 }
129
130 pub(crate) fn wt(&self) -> *const c_void {
131 self.0.wt
132 }
133}
134
135impl CommandsHandle {
136 pub fn type_name(&self) -> String {
138 crate::diag::type_name(self.0.meta)
139 }
140}
141
142struct Content<'a> {
146 builder: &'a dyn Fn() -> ViewHandle,
147}
148
149unsafe extern "C" fn emit_content(out: *mut u8, payload: *mut c_void) {
157 let p = unsafe { &*(payload as *const Content<'_>) };
158 let view = (p.builder)();
159 unsafe { *(out as *mut u64) = view.into_anyview_word() };
160}
161
162unsafe fn conformance(
171 meta: *const c_void,
172 proto: *const c_void,
173 name: &str,
174) -> *const c_void {
175 let wt = unsafe { abi::get_view_wt_for_proto(meta, proto) };
176 assert!(!wt.is_null(), "{} !: {name}", crate::diag::type_name(meta));
177 wt
178}
179
180unsafe fn over_anyview(ma: *const c_void) -> *const c_void {
185 let meta = unsafe {
186 abi::generic_metadata2(ma, resolve::anyview_meta(), resolve::anyview_wt())
187 };
188 assert!(!meta.is_null(), "metadata instantiation failed");
189 meta
190}
191
192unsafe fn build_from_content(
201 init: *const c_void,
202 meta: *const c_void,
203 builder: &dyn Fn() -> ViewHandle,
204) -> Vec<u8> {
205 unsafe {
206 let payload = Content { builder };
207 let mut ctx = abi::ClosureCtx::new(
208 emit_content,
209 &payload as *const Content<'_> as *mut c_void,
210 );
211 let mut bytes = vec![0u8; abi::type_bytes(meta)];
212 core::arch::asm!("blr {f}", f = in(reg) init,
213 in("x0") abi::swift_closure_thunk as *const c_void,
214 in("x1") &mut ctx as *mut abi::ClosureCtx,
215 in("x2") resolve::anyview_meta(),
216 in("x3") resolve::anyview_wt(),
217 in("x8") bytes.as_mut_ptr(),
218 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
219 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
220 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
221 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
222 clobber_abi("C"),
223 );
224 bytes
225 }
226}
227
228#[cfg(target_os = "macos")]
239unsafe fn build_from_key_string_content(
240 init: *const c_void,
241 meta: *const c_void,
242 key: &str,
243 string: &str,
244 builder: &dyn Fn() -> ViewHandle,
245) -> Vec<u8> {
246 unsafe {
247 let (k0, k1, k2, k3) = views::lsk_words(key);
248 let s = abi::swift_string(string);
249 let s0 = u64::from_le_bytes(s[..8].try_into().expect("string lo"));
250 let s1 = u64::from_le_bytes(s[8..].try_into().expect("string hi"));
251 let payload = Content { builder };
252 let mut ctx = abi::ClosureCtx::new(
253 emit_content,
254 &payload as *const Content<'_> as *mut c_void,
255 );
256 let mut bytes = vec![0u8; abi::type_bytes(meta)];
257 core::arch::asm!(
260 "sub sp, sp, #16",
261 "stp {cm}, {cw}, [sp]",
262 "blr {f}",
263 "add sp, sp, #16",
264 f = in(reg) init,
265 cm = in(reg) resolve::anyview_meta(),
266 cw = in(reg) resolve::anyview_wt(),
267 in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
268 in("x4") s0, in("x5") s1,
269 in("x6") abi::swift_closure_thunk as *const c_void,
270 in("x7") &mut ctx as *mut abi::ClosureCtx,
271 in("x8") bytes.as_mut_ptr(),
272 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
273 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
274 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
275 clobber_abi("C"),
276 );
277 bytes
278 }
279}
280
281pub fn window_group<V: IntoView>(content: impl Fn() -> V) -> SceneHandle {
286 let content = move || content().into_view().into_handle();
287 unsafe {
288 let meta = over_anyview(resolve::windowgroup_ma());
289 let bytes = build_from_content(resolve::windowgroup_content_init(), meta, &content);
290 SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
291 }
292}
293
294#[cfg(target_os = "macos")]
296pub fn settings<V: IntoView>(content: impl Fn() -> V) -> SceneHandle {
297 let content = move || content().into_view().into_handle();
298 unsafe {
299 let meta = over_anyview(resolve::settings_ma());
300 let bytes = build_from_content(resolve::settings_content_init(), meta, &content);
301 SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
302 }
303}
304
305#[cfg(target_os = "macos")]
310pub fn window<V: IntoView>(title: &str, id: &str, content: impl Fn() -> V) -> SceneHandle {
311 let content = move || content().into_view().into_handle();
312 unsafe {
313 let meta = over_anyview(resolve::window_ma());
314 let bytes =
315 build_from_key_string_content(resolve::window_init(), meta, title, id, &content);
316 SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
317 }
318}
319
320#[cfg(target_os = "macos")]
329pub fn menu_bar_extra<V: IntoView>(
330 title: &str,
331 system_image: &str,
332 content: impl Fn() -> V,
333) -> SceneHandle {
334 let content = move || content().into_view().into_handle();
335 unsafe {
336 let label = mangle::bound(
337 &swift_type!(SwiftUI.Label),
338 &[&swift_type!(SwiftUI.Text), &swift_type!(SwiftUI.Image)],
339 );
340 let named = mangle::bound(
341 &swift_type!(SwiftUI.MenuBarExtra),
342 &[&label, &swift_type!(SwiftUI.AnyView)],
343 );
344 let meta = abi::resolve_type_by_mangled_name(named.as_bytes());
345 assert!(!meta.is_null(), "{}", mangle::demangle(&named));
346 let bytes = build_from_key_string_content(
347 resolve::menu_bar_extra_init(),
348 meta,
349 title,
350 system_image,
351 &content,
352 );
353 SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
354 }
355}
356
357pub fn group(scenes: Vec<SceneHandle>) -> SceneHandle {
363 assert!(!scenes.is_empty(), "a scene group needs at least one scene");
364 if scenes.len() == 1 {
365 return scenes.into_iter().next().expect("one scene");
366 }
367 unsafe {
368 SceneHandle(tuple_of(
369 scenes.into_iter().map(|s| s.0).collect(),
370 resolve::tuple_scene_ma(),
371 resolve::tuple_scene_init(),
372 resolve::scene_proto(),
373 "Scene",
374 ))
375 }
376}
377
378unsafe fn tuple_of(
393 parts: Vec<Value>,
394 ma: *const c_void,
395 init: *const c_void,
396 proto: *const c_void,
397 name: &str,
398) -> Value {
399 unsafe {
400 let elements: Vec<*const c_void> = parts.iter().map(|p| p.meta).collect();
401 let tuple_meta = abi::tuple_metadata(&elements);
402 assert!(!tuple_meta.is_null(), "tuple metadata instantiation failed");
403 let offsets = abi::tuple_element_offsets(tuple_meta, parts.len());
405 let mut tuple = vec![0u8; abi::type_bytes(tuple_meta)];
406 for (part, &offset) in parts.iter().zip(&offsets) {
407 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(part.meta);
408 if vwt.size > 0 {
409 (vwt.initialize_with_copy)(
410 tuple.as_mut_ptr().add(offset) as *mut c_void,
411 part.ptr() as *mut c_void,
412 part.meta,
413 );
414 }
415 }
416
417 let meta = abi::generic_metadata1(ma, tuple_meta);
418 assert!(!meta.is_null(), "tuple wrapper metadata instantiation failed");
419 let mut bytes = vec![0u8; abi::type_bytes(meta)];
420 core::arch::asm!("blr {f}", f = in(reg) init,
422 in("x0") tuple.as_mut_ptr(),
423 in("x1") tuple_meta,
424 in("x8") bytes.as_mut_ptr(),
425 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
426 lateout("x6") _, lateout("x7") _,
427 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
428 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
429 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
430 clobber_abi("C"),
431 );
432 drop(parts);
435 Value::wrap(bytes, meta, conformance(meta, proto, name))
436 }
437}
438
439pub fn command_menu<V: IntoView>(title: &str, content: impl Fn() -> V) -> CommandsHandle {
443 let content = move || content().into_view().into_handle();
444 unsafe {
445 let meta = over_anyview(resolve::command_menu_ma());
446 let builder: &dyn Fn() -> ViewHandle = &content;
447 let (k0, k1, k2, k3) = views::lsk_words(title);
448 let payload = Content { builder };
449 let mut ctx = abi::ClosureCtx::new(
450 emit_content,
451 &payload as *const Content<'_> as *mut c_void,
452 );
453 let mut bytes = vec![0u8; abi::type_bytes(meta)];
454 core::arch::asm!("blr {f}", f = in(reg) resolve::command_menu_init(),
457 in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
458 in("x4") abi::swift_closure_thunk as *const c_void,
459 in("x5") &mut ctx as *mut abi::ClosureCtx,
460 in("x6") resolve::anyview_meta(),
461 in("x7") resolve::anyview_wt(),
462 in("x8") bytes.as_mut_ptr(),
463 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
464 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
465 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
466 clobber_abi("C"),
467 );
468 CommandsHandle(Value::wrap(
469 bytes,
470 meta,
471 conformance(meta, resolve::commands_proto(), "Commands"),
472 ))
473 }
474}
475
476swift_statics! {
477 pub enum CommandPlacement {
483 AppInfo = c"$s7SwiftUI21CommandGroupPlacementV7appInfoACvgZ",
484 AppSettings = c"$s7SwiftUI21CommandGroupPlacementV11appSettingsACvgZ",
485 AppShortcuts = c"$s7SwiftUI21CommandGroupPlacementV12appShortcutsACvgZ",
486 AppSpecificSettings = c"$s7SwiftUI21CommandGroupPlacementV11appSettingsACvgZ",
487 AppTermination = c"$s7SwiftUI21CommandGroupPlacementV14appTerminationACvgZ",
488 AppVisibility = c"$s7SwiftUI21CommandGroupPlacementV13appVisibilityACvgZ",
489 Help = c"$s7SwiftUI21CommandGroupPlacementV4helpACvgZ",
490 ImportExport = c"$s7SwiftUI21CommandGroupPlacementV12importExportACvgZ",
491 NewItem = c"$s7SwiftUI21CommandGroupPlacementV7newItemACvgZ",
492 Pasteboard = c"$s7SwiftUI21CommandGroupPlacementV10pasteboardACvgZ",
493 PrintItem = c"$s7SwiftUI21CommandGroupPlacementV9printItemACvgZ",
494 SaveItem = c"$s7SwiftUI21CommandGroupPlacementV8saveItemACvgZ",
495 Sidebar = c"$s7SwiftUI21CommandGroupPlacementV7sidebarACvgZ",
496 SingleWindowList = c"$s7SwiftUI21CommandGroupPlacementV16singleWindowListACvgZ",
497 SystemServices = c"$s7SwiftUI21CommandGroupPlacementV14systemServicesACvgZ",
498 TextEditing = c"$s7SwiftUI21CommandGroupPlacementV11textEditingACvgZ",
499 TextFormatting = c"$s7SwiftUI21CommandGroupPlacementV14textFormattingACvgZ",
500 Toolbar = c"$s7SwiftUI21CommandGroupPlacementV7toolbarACvgZ",
501 UndoRedo = c"$s7SwiftUI21CommandGroupPlacementV8undoRedoACvgZ",
502 WindowArrangement = c"$s7SwiftUI21CommandGroupPlacementV17windowArrangementACvgZ",
503 WindowList = c"$s7SwiftUI21CommandGroupPlacementV10windowListACvgZ",
504 WindowSize = c"$s7SwiftUI21CommandGroupPlacementV10windowSizeACvgZ",
505 }
506}
507
508#[derive(Clone, Copy, Debug, PartialEq, Eq)]
510pub enum Relative {
511 Replacing,
513 After,
515 Before,
517}
518
519pub fn command_group<V: IntoView>(
522 placement: CommandPlacement,
523 where_: Relative,
524 content: impl Fn() -> V,
525) -> CommandsHandle {
526 let content = move || content().into_view().into_handle();
527 unsafe {
528 let meta = over_anyview(resolve::command_group_ma());
529 let placement_meta = abi::metadata0(resolve::command_group_placement_ma());
530 assert!(!placement_meta.is_null(), "CommandGroupPlacement metadata");
531 let mut place =
533 views::static_getter_indirect(placement.resolved(), placement_meta, 0);
534
535 let init = match where_ {
536 Relative::Replacing => resolve::command_group_replacing_init(),
537 Relative::After => resolve::command_group_after_init(),
538 Relative::Before => resolve::command_group_before_init(),
539 };
540 let builder: &dyn Fn() -> ViewHandle = &content;
541 let payload = Content { builder };
542 let mut ctx = abi::ClosureCtx::new(
543 emit_content,
544 &payload as *const Content<'_> as *mut c_void,
545 );
546 let mut bytes = vec![0u8; abi::type_bytes(meta)];
547 core::arch::asm!("blr {f}", f = in(reg) init,
550 in("x0") place.as_mut_ptr(),
551 in("x1") abi::swift_closure_thunk as *const c_void,
552 in("x2") &mut ctx as *mut abi::ClosureCtx,
553 in("x3") resolve::anyview_meta(),
554 in("x4") resolve::anyview_wt(),
555 in("x8") bytes.as_mut_ptr(),
556 lateout("x5") _, lateout("x6") _, lateout("x7") _,
557 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
558 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
559 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
560 clobber_abi("C"),
561 );
562 CommandsHandle(Value::wrap(
563 bytes,
564 meta,
565 conformance(meta, resolve::commands_proto(), "Commands"),
566 ))
567 }
568}
569
570pub fn commands(scene: SceneHandle, menus: Vec<CommandsHandle>) -> SceneHandle {
577 assert!(!menus.is_empty(), "`commands` needs at least one menu");
578 unsafe {
579 let content = if menus.len() == 1 {
580 menus.into_iter().next().expect("one menu").0
581 } else {
582 tuple_of(
583 menus.into_iter().map(|m| m.0).collect(),
584 resolve::tuple_commands_ma(),
585 resolve::tuple_commands_init(),
586 resolve::commands_proto(),
587 "Commands",
588 )
589 };
590
591 let payload = EmitValue { value: content.ptr(), meta: content.meta };
594 let mut ctx = abi::ClosureCtx::new(
595 emit_value,
596 &payload as *const EmitValue as *mut c_void,
597 );
598
599 let subst = [scene.meta(), content.meta, scene.wt(), content.wt];
600 let (meta, wt, size) =
601 views::opaque_result_type(resolve::scene_commands_oret(), &subst);
602 let mut bytes = vec![0u8; size];
603 let this = scene.0.ptr();
604 core::arch::asm!("blr {f}", f = in(reg) resolve::scene_commands_fn(),
607 in("x0") abi::swift_closure_thunk as *const c_void,
608 in("x1") &mut ctx as *mut abi::ClosureCtx,
609 in("x2") scene.meta(),
610 in("x3") content.meta,
611 in("x4") scene.wt(),
612 in("x5") content.wt,
613 in("x8") bytes.as_mut_ptr(),
614 in("x20") this,
615 lateout("x6") _, lateout("x7") _,
616 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
617 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
618 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
619 );
620 SceneHandle(Value::wrap(bytes, meta, wt))
621 }
622}
623
624#[repr(C)]
626struct EmitValue {
627 value: *const c_void,
628 meta: *const c_void,
629}
630
631unsafe extern "C" fn emit_value(out: *mut u8, payload: *mut c_void) {
641 let p = unsafe { &*(payload as *const EmitValue) };
642 unsafe {
643 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(p.meta);
644 if vwt.size > 0 {
645 (vwt.initialize_with_copy)(out as *mut c_void, p.value as *mut c_void, p.meta);
646 }
647 }
648}