1use crate::abi;
10use crate::objc::{self, NSRect};
11use crate::resolve;
12use crate::views::ViewHandle;
13use core::ffi::c_void;
14
15pub struct HostingView {
17 ptr: *mut c_void,
19 #[cfg(not(target_os = "macos"))]
21 controller: *mut c_void,
22 window: core::cell::Cell<*mut c_void>,
30}
31
32impl HostingView {
33 #[cfg(target_os = "macos")]
35 pub fn new(view: &ViewHandle) -> Self {
36 let _pool = objc::AutoreleasePool::new();
37 Self {
38 ptr: unsafe { make_hosting_view(view) },
39 window: core::cell::Cell::new(core::ptr::null_mut()),
40 }
41 }
42
43 #[cfg(not(target_os = "macos"))]
45 pub fn new(view: &ViewHandle) -> Self {
46 let _pool = objc::AutoreleasePool::new();
47 unsafe {
48 let controller = make_hosting_controller(view);
49 let ptr = objc::send(controller, objc::sel(c"view"));
51 assert!(!ptr.is_null(), "UIHostingController produced no view");
52 Self { ptr, controller, window: core::cell::Cell::new(core::ptr::null_mut()) }
53 }
54 }
55
56 pub fn as_ptr(&self) -> *mut c_void {
58 self.ptr
59 }
60
61 fn root_owner(&self) -> *mut c_void {
63 #[cfg(target_os = "macos")]
64 {
65 self.ptr
66 }
67 #[cfg(not(target_os = "macos"))]
68 {
69 self.controller
70 }
71 }
72
73 #[cfg(not(target_os = "macos"))]
75 pub fn controller(&self) -> *mut c_void {
76 self.controller
77 }
78
79 pub fn set_root(&self, view: &ViewHandle) {
83 let _pool = objc::AutoreleasePool::new();
84 unsafe {
85 let mut incoming: u64 = view.to_anyview_word();
94 core::arch::asm!("blr {f}", f = in(reg) resolve::hosting_root_setter(),
95 in("x0") &mut incoming as *mut u64,
96 in("x20") self.root_owner(),
97 lateout("x1") _, lateout("x2") _, lateout("x3") _,
98 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
99 lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
100 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
101 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
102 clobber_abi("C"),
103 );
104 }
105 }
106
107 pub fn set_frame(&self, width: f64, height: f64) {
109 let _pool = objc::AutoreleasePool::new();
110 unsafe {
111 objc::send_rect(
112 self.ptr,
113 objc::sel(c"setFrame:"),
114 NSRect::new(0.0, 0.0, width, height),
115 );
116 #[cfg(target_os = "macos")]
117 objc::send(self.ptr, objc::sel(c"layoutSubtreeIfNeeded"));
118 #[cfg(not(target_os = "macos"))]
119 {
120 objc::send(self.ptr, objc::sel(c"setNeedsLayout"));
121 objc::send(self.ptr, objc::sel(c"layoutIfNeeded"));
122 }
123 }
124 }
125
126 #[cfg(not(target_os = "macos"))]
134 pub fn set_safe_area(&self, enabled: bool) {
135 unsafe {
136 let regions: u64 = if enabled { u64::MAX } else { 0 };
139 core::arch::asm!("blr {f}", f = in(reg) resolve::hosting_ctrl_safe_area(),
140 in("x0") regions,
141 in("x20") self.controller,
142 lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
143 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
144 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
145 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
146 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
147 );
148 }
149 }
150
151 #[cfg(target_os = "macos")]
153 pub fn fitting_size(&self) -> (f64, f64) {
154 let s = unsafe { objc::send_ret_size(self.ptr, objc::sel(c"fittingSize")) };
155 (s.width, s.height)
156 }
157
158 #[cfg(not(target_os = "macos"))]
164 pub fn fitting_size(&self) -> (f64, f64) {
165 unsafe {
166 let (w, h): (f64, f64);
167 core::arch::asm!("blr {f}", f = in(reg) resolve::hosting_ctrl_size(),
168 in("d0") 0.0f64, in("d1") 0.0f64,
169 in("x20") self.controller,
170 lateout("d0") w, lateout("d1") h,
171 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
172 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
173 lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
174 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
175 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
176 clobber_abi("C"),
177 );
178 (w, h)
179 }
180 }
181}
182
183impl HostingView {
184 pub(crate) unsafe fn offscreen(&self, width: f64, height: f64) -> *mut c_void {
190 unsafe { offscreen_window(self, width, height) }
191 }
192}
193
194impl Drop for HostingView {
195 fn drop(&mut self) {
196 let _pool = objc::AutoreleasePool::new();
197 unsafe {
198 let window = self.window.get();
199 if !window.is_null() {
200 #[cfg(target_os = "macos")]
201 {
202 objc::send(window, objc::sel(c"close"));
206 }
207 #[cfg(not(target_os = "macos"))]
208 {
209 objc::send_bool(window, objc::sel(c"setHidden:"), true);
212 objc::send(window, objc::sel(c"release"));
213 }
214 }
215 }
216 unsafe {
222 #[cfg(target_os = "macos")]
223 objc::send(self.ptr, objc::sel(c"release"));
224 #[cfg(not(target_os = "macos"))]
225 objc::send(self.controller, objc::sel(c"release"));
226 }
227 }
228}
229
230#[cfg(target_os = "macos")]
232unsafe fn make_hosting_view(view: &ViewHandle) -> *mut c_void {
233 unsafe {
239 let meta = abi::generic_metadata2(
240 resolve::hosting_view_ma(),
241 resolve::anyview_meta(),
242 resolve::anyview_wt(),
243 );
244 init_root_view(resolve::hosting_view_init(), meta, view, "NSHostingView")
245 }
246}
247
248#[cfg(not(target_os = "macos"))]
250unsafe fn make_hosting_controller(view: &ViewHandle) -> *mut c_void {
251 unsafe {
252 let meta = abi::generic_metadata2(
253 resolve::hosting_ctrl_ma(),
254 resolve::anyview_meta(),
255 resolve::anyview_wt(),
256 );
257 init_root_view(resolve::hosting_ctrl_init(), meta, view, "UIHostingController")
258 }
259}
260
261unsafe fn init_root_view(
263 init: *const c_void,
264 meta: *const c_void,
265 view: &ViewHandle,
266 what: &str,
267) -> *mut c_void {
268 unsafe {
269 let mut storage: u64 = view.to_anyview_word();
270 swift_runtime_sys::RuntimeRaw::swift_retain(storage as *mut c_void);
271 let out: *mut c_void;
272 core::arch::asm!("blr {f}", f = in(reg) init,
273 in("x0") &mut storage as *mut u64,
274 in("x20") meta,
275 lateout("x0") out,
276 lateout("x1") _, lateout("x2") _, lateout("x3") _,
277 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
278 lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
279 lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
280 lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
281 clobber_abi("C"),
282 );
283 assert!(!out.is_null(), "{what} creation failed");
284 out
285 }
286}
287
288#[cfg(target_os = "macos")]
294unsafe fn offscreen_window(hv: &HostingView, width: f64, height: f64) -> *mut c_void {
295 unsafe {
296 let bounds = NSRect::new(0.0, 0.0, width, height);
297 let existing = hv.window.get();
298 if !existing.is_null() {
299 type SetFrame = unsafe extern "C" fn(*mut c_void, *const c_void, NSRect, bool);
300 let set_frame: SetFrame = core::mem::transmute(crate::objc::msg_send_addr());
301 set_frame(existing, objc::sel(c"setFrame:display:"), bounds, false);
302 return existing;
303 }
304
305 let _ = objc::shared_app();
306 let win = objc::send(objc::class(c"OBJC_CLASS_$_NSWindow"), objc::sel(c"alloc"));
307 type InitWin =
308 unsafe extern "C" fn(*mut c_void, *const c_void, NSRect, u64, u64, bool) -> *mut c_void;
309 let init: InitWin = core::mem::transmute(crate::objc::msg_send_addr());
310 let win = init(
312 win,
313 objc::sel(c"initWithContentRect:styleMask:backing:defer:"),
314 bounds,
315 0,
316 2,
317 false,
318 );
319 let name = objc::nsstring("NSAppearanceNameAqua");
320 let appearance = objc::send_id(
321 objc::class(c"OBJC_CLASS_$_NSAppearance"),
322 objc::sel(c"appearanceNamed:"),
323 name,
324 );
325 if !appearance.is_null() {
326 objc::send_id(win, objc::sel(c"setAppearance:"), appearance);
327 }
328 objc::send_id(win, objc::sel(c"setContentView:"), hv.as_ptr());
329 hv.window.set(win);
330 win
331 }
332}
333
334#[cfg(not(target_os = "macos"))]
336unsafe fn offscreen_window(hv: &HostingView, width: f64, height: f64) -> *mut c_void {
337 unsafe {
338 let bounds = NSRect::new(0.0, 0.0, width, height);
339 let existing = hv.window.get();
340 if !existing.is_null() {
341 objc::send_rect(existing, objc::sel(c"setFrame:"), bounds);
342 return existing;
343 }
344 let window = objc::send(objc::class(c"OBJC_CLASS_$_UIWindow"), objc::sel(c"alloc"));
345 type InitFrame = unsafe extern "C" fn(*mut c_void, *const c_void, NSRect) -> *mut c_void;
346 let init: InitFrame = core::mem::transmute(crate::objc::msg_send_addr());
347 let window = init(window, objc::sel(c"initWithFrame:"), bounds);
348 hv.set_safe_area(false);
351 objc::send_id(window, objc::sel(c"addSubview:"), hv.as_ptr());
356 objc::send_bool(window, objc::sel(c"setHidden:"), false);
359 hv.window.set(window);
360 window
361 }
362}