#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
#![warn(unused_extern_crates)]
#![warn(missing_docs)]
use core::fmt;
use libloading::*;
use parking_lot::Mutex;
use std::{env, io, mem, path::PathBuf};
use zng_view_api::StaticPatch;
zng_env::on_process_start!(|_| {
if std::env::var("ZNG_VIEW_NO_INIT_START").is_err() {
view_process_main()
}
});
pub fn view_process_main() {
ViewLib::install().unwrap().view_process_main()
}
pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) -> ! {
ViewLib::install().unwrap().run_same_process(run_app)
}
pub struct ViewLib {
view_process_main_fn: unsafe extern "C" fn(&StaticPatch),
run_same_process_fn: unsafe extern "C" fn(&StaticPatch, extern "C" fn()),
_lib: Library,
}
impl ViewLib {
pub fn install() -> Result<Self, Error> {
let dir = env::temp_dir().join("zng_view");
std::fs::create_dir_all(&dir)?;
Self::install_to(dir)
}
pub fn uninstall() -> Result<bool, io::Error> {
let dir = env::temp_dir().join("zng_view");
Self::uninstall_from(dir)
}
pub fn install_to(dir: impl Into<PathBuf>) -> Result<Self, Error> {
Self::install_to_impl(dir.into())
}
fn install_to_impl(dir: PathBuf) -> Result<Self, Error> {
#[cfg(not(zng_lib_embedded))]
{
let _ = dir;
panic!("library not embedded");
}
#[cfg(zng_lib_embedded)]
{
let file = Self::install_path(dir);
if !file.exists() {
std::fs::write(&file, LIB)?;
}
Self::link(file)
}
}
pub fn uninstall_from(dir: impl Into<PathBuf>) -> Result<bool, io::Error> {
Self::uninstall_from_impl(dir.into())
}
fn uninstall_from_impl(dir: PathBuf) -> Result<bool, io::Error> {
#[cfg(not(zng_lib_embedded))]
{
let _ = dir;
Ok(false)
}
#[cfg(zng_lib_embedded)]
{
let file = Self::install_path(dir);
if file.exists() {
std::fs::remove_file(file)?;
Ok(true)
} else {
Ok(false)
}
}
}
#[cfg(zng_lib_embedded)]
fn install_path(dir: PathBuf) -> PathBuf {
#[cfg(target_os = "windows")]
let file_name = format!("{LIB_NAME}.dll");
#[cfg(target_os = "linux")]
let file_name = format!("{LIB_NAME}.so");
#[cfg(target_os = "macos")]
let file_name = format!("{LIB_NAME}.dylib");
dir.join(file_name)
}
pub fn link(view_dylib: impl Into<PathBuf>) -> Result<Self, Error> {
Self::link_impl(view_dylib.into())
}
fn link_impl(mut lib: PathBuf) -> Result<Self, Error> {
if !lib.exists() && lib.extension().is_none() {
#[cfg(target_os = "windows")]
lib.set_extension("dll");
#[cfg(target_os = "linux")]
lib.set_extension("so");
#[cfg(target_os = "macos")]
lib.set_extension("dylib");
}
if lib.exists() {
lib = dunce::canonicalize(lib)?;
}
if !lib.exists() {
return Err(io::Error::new(io::ErrorKind::NotFound, format!("view library not found in `{}`", lib.display())).into());
}
unsafe {
let lib = Library::new(lib)?;
Ok(ViewLib {
view_process_main_fn: *match lib.get(b"extern_view_process_main") {
Ok(f) => f,
Err(e) => match lib.get(b"extern_init") {
Ok(f) => f,
Err(_) => return Err(e.into()),
},
},
run_same_process_fn: *lib.get(b"extern_run_same_process")?,
_lib: lib,
})
}
}
pub fn view_process_main(self) {
let patch = StaticPatch::capture();
unsafe { (self.view_process_main_fn)(&patch) }
}
pub fn run_same_process(self, run_app: impl FnOnce() + Send + 'static) -> ! {
self.run_same_process_impl(Box::new(run_app))
}
fn run_same_process_impl(self, run_app: Box<dyn FnOnce() + Send>) -> ! {
let patch = StaticPatch::capture();
enum Run {
Waiting,
Set(Box<dyn FnOnce() + Send>),
Taken,
}
static RUN: Mutex<Run> = Mutex::new(Run::Waiting);
match mem::replace(&mut *RUN.lock(), Run::Set(Box::new(run_app))) {
Run::Waiting => {}
_ => panic!("expected only one call to `run_same_process`"),
};
extern "C" fn run() {
match mem::replace(&mut *RUN.lock(), Run::Taken) {
Run::Set(run_app) => run_app(),
_ => unreachable!(),
}
}
unsafe {
(self.run_same_process_fn)(&patch, run);
}
zng_env::exit(0)
}
}
#[cfg(zng_lib_embedded)]
const LIB: &[u8] = include_bytes!(env!("ZNG_VIEW_LIB"));
#[cfg(zng_lib_embedded)]
const LIB_NAME: &str = concat!("zv.", env!("CARGO_PKG_VERSION"), ".", env!("ZNG_VIEW_LIB_HASH"));
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Io(io::Error),
Lib(libloading::Error),
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Io(e)
}
}
impl From<libloading::Error> for Error {
fn from(e: libloading::Error) -> Self {
Error::Lib(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(e) => write!(f, "{e}"),
Error::Lib(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(e) => Some(e),
Error::Lib(e) => Some(e),
}
}
}