#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/master/examples/res/image/zng-logo-icon.png")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/master/examples/res/image/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 std::{env, io, path::PathBuf};
pub fn init() {
ViewLib::install().unwrap().init()
}
pub fn run_same_process(run_app: impl FnOnce() + Send + 'static) -> ! {
ViewLib::install().unwrap().run_same_process(run_app)
}
pub struct ViewLib {
init_fn: unsafe extern "C" fn(),
run_same_process_fn: unsafe extern "C" fn(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 = lib.canonicalize()?;
}
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 {
init_fn: *lib.get(b"extern_init")?,
run_same_process_fn: *lib.get(b"extern_run_same_process")?,
_lib: lib,
})
}
}
pub fn init(self) {
unsafe { (self.init_fn)() }
}
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>) -> ! {
unsafe {
use std::sync::atomic::{AtomicU8, Ordering};
static STATE: AtomicU8 = AtomicU8::new(ST_NONE);
const ST_NONE: u8 = 0;
const ST_SOME: u8 = 1;
const ST_TAKEN: u8 = 2;
static mut RUN: Option<Box<dyn FnOnce() + Send>> = None;
if STATE.swap(ST_SOME, Ordering::SeqCst) != ST_NONE {
panic!("expected only one call to `run_same_process`");
}
RUN = Some(run_app);
extern "C" fn run() {
if STATE.swap(ST_TAKEN, Ordering::SeqCst) != ST_SOME {
panic!("expected only one call to `run_app` closure");
}
let run_app = unsafe { RUN.take() }.unwrap();
run_app();
}
(self.run_same_process_fn)(run)
}
std::process::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)]
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),
}
}
}