1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
11#![warn(unused_extern_crates)]
12#![warn(missing_docs)]
13
14use core::fmt;
15use libloading::*;
16use parking_lot::Mutex;
17use std::{env, io, mem, path::PathBuf};
18use zng_view_api::StaticPatch;
19
20zng_env::on_process_start!(|_| {
21 if std::env::var("ZNG_VIEW_NO_INIT_START").is_err() {
22 view_process_main()
23 }
24});
25
26pub fn view_process_main() {
36 ViewLib::install().unwrap().view_process_main()
37}
38
39pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) -> ! {
55 ViewLib::install().unwrap().run_same_process(run_app)
56}
57
58pub struct ViewLib {
60 view_process_main_fn: unsafe extern "C" fn(&StaticPatch),
61 run_same_process_fn: unsafe extern "C" fn(&StaticPatch, extern "C" fn()),
62 _lib: Library,
63}
64impl ViewLib {
65 pub fn install() -> Result<Self, Error> {
67 let dir = env::temp_dir().join("zng_view");
68 std::fs::create_dir_all(&dir)?;
69 Self::install_to(dir)
70 }
71
72 pub fn uninstall() -> Result<bool, io::Error> {
78 let dir = env::temp_dir().join("zng_view");
79 Self::uninstall_from(dir)
80 }
81
82 pub fn install_to(dir: impl Into<PathBuf>) -> Result<Self, Error> {
84 Self::install_to_impl(dir.into())
85 }
86 fn install_to_impl(dir: PathBuf) -> Result<Self, Error> {
87 #[cfg(not(zng_lib_embedded))]
88 {
89 let _ = dir;
90 panic!("library not embedded");
91 }
92
93 #[cfg(zng_lib_embedded)]
94 {
95 let file = Self::install_path(dir);
96
97 if !file.exists() {
98 std::fs::write(&file, LIB)?;
99 }
100
101 Self::link(file)
102 }
103 }
104
105 pub fn uninstall_from(dir: impl Into<PathBuf>) -> Result<bool, io::Error> {
113 Self::uninstall_from_impl(dir.into())
114 }
115 fn uninstall_from_impl(dir: PathBuf) -> Result<bool, io::Error> {
116 #[cfg(not(zng_lib_embedded))]
117 {
118 let _ = dir;
119 Ok(false)
120 }
121
122 #[cfg(zng_lib_embedded)]
123 {
124 let file = Self::install_path(dir);
125
126 if file.exists() {
127 std::fs::remove_file(file)?;
128 Ok(true)
129 } else {
130 Ok(false)
131 }
132 }
133 }
134
135 #[cfg(zng_lib_embedded)]
136 fn install_path(dir: PathBuf) -> PathBuf {
137 #[cfg(target_os = "windows")]
138 let file_name = format!("{LIB_NAME}.dll");
139 #[cfg(target_os = "linux")]
140 let file_name = format!("{LIB_NAME}.so");
141 #[cfg(target_os = "macos")]
142 let file_name = format!("{LIB_NAME}.dylib");
143
144 dir.join(file_name)
145 }
146
147 pub fn link(view_dylib: impl Into<PathBuf>) -> Result<Self, Error> {
155 Self::link_impl(view_dylib.into())
156 }
157 fn link_impl(mut lib: PathBuf) -> Result<Self, Error> {
158 if !lib.exists() && lib.extension().is_none() {
159 #[cfg(target_os = "windows")]
160 lib.set_extension("dll");
161 #[cfg(target_os = "linux")]
162 lib.set_extension("so");
163 #[cfg(target_os = "macos")]
164 lib.set_extension("dylib");
165 }
166
167 if lib.exists() {
168 lib = dunce::canonicalize(lib)?;
170 }
171
172 if !lib.exists() {
173 return Err(io::Error::new(io::ErrorKind::NotFound, format!("view library not found in `{}`", lib.display())).into());
174 }
175
176 unsafe {
177 let lib = Library::new(lib)?;
178 Ok(ViewLib {
179 view_process_main_fn: *match lib.get(b"extern_view_process_main") {
180 Ok(f) => f,
181 Err(e) => match lib.get(b"extern_init") {
183 Ok(f) => f,
184 Err(_) => return Err(e.into()),
185 },
186 },
187 run_same_process_fn: *lib.get(b"extern_run_same_process")?,
188 _lib: lib,
189 })
190 }
191 }
192
193 pub fn view_process_main(self) {
202 let patch = StaticPatch::capture();
203 unsafe { (self.view_process_main_fn)(&patch) }
204 }
205
206 pub fn run_same_process(self, run_app: impl FnOnce() + Send + 'static) -> ! {
217 self.run_same_process_impl(Box::new(run_app))
218 }
219 fn run_same_process_impl(self, run_app: Box<dyn FnOnce() + Send>) -> ! {
220 let patch = StaticPatch::capture();
221
222 enum Run {
223 Waiting,
224 Set(Box<dyn FnOnce() + Send>),
225 Taken,
226 }
227 static RUN: Mutex<Run> = Mutex::new(Run::Waiting);
228
229 match mem::replace(&mut *RUN.lock(), Run::Set(Box::new(run_app))) {
230 Run::Waiting => {}
231 _ => panic!("expected only one call to `run_same_process`"),
232 };
233
234 extern "C" fn run() {
235 match mem::replace(&mut *RUN.lock(), Run::Taken) {
236 Run::Set(run_app) => run_app(),
237 _ => unreachable!(),
238 }
239 }
240
241 unsafe {
243 (self.run_same_process_fn)(&patch, run);
244 }
245
246 zng_env::exit(0)
248 }
249}
250
251#[cfg(zng_lib_embedded)]
252const LIB: &[u8] = include_bytes!(env!("ZNG_VIEW_LIB"));
253#[cfg(zng_lib_embedded)]
254const LIB_NAME: &str = concat!("zv.", env!("CARGO_PKG_VERSION"), ".", env!("ZNG_VIEW_LIB_HASH"));
255
256#[derive(Debug)]
258#[non_exhaustive]
259pub enum Error {
260 Io(io::Error),
262 Lib(libloading::Error),
264}
265impl From<io::Error> for Error {
266 fn from(e: io::Error) -> Self {
267 Error::Io(e)
268 }
269}
270impl From<libloading::Error> for Error {
271 fn from(e: libloading::Error) -> Self {
272 Error::Lib(e)
273 }
274}
275impl fmt::Display for Error {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 match self {
278 Error::Io(e) => write!(f, "{e}"),
279 Error::Lib(e) => write!(f, "{e}"),
280 }
281 }
282}
283impl std::error::Error for Error {
284 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
285 match self {
286 Error::Io(e) => Some(e),
287 Error::Lib(e) => Some(e),
288 }
289 }
290}