#[cfg(feature = "dl")]
mod dl_impl {
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::sync::PoisonError;
#[derive(Debug)]
pub enum OpenError {
Library(libloading::Error),
InternalError,
}
impl Display for OpenError {
fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
let detail = match self {
OpenError::Library(detail) => {
f.write_str("Library")?;
detail.to_string()
}
OpenError::InternalError => {
f.write_str("InternalError")?;
String::new()
}
};
if !detail.is_empty() {
f.write_fmt(format_args!(" ({})", detail))?;
}
Ok(())
}
}
impl Error for OpenError {}
impl From<libloading::Error> for OpenError {
fn from(value: libloading::Error) -> Self {
OpenError::Library(value)
}
}
impl<T> From<PoisonError<T>> for OpenError {
fn from(_: PoisonError<T>) -> Self {
OpenError::InternalError
}
}
pub unsafe fn load_library(
prefix: Option<&str>,
names: &[&str],
) -> Result<libloading::Library, libloading::Error> {
use std::path::{Path, PathBuf};
debug_assert!(!names.is_empty());
let mut last_error = None;
if prefix.is_some() {
match load_library(None, names) {
Ok(lib) => return Ok(lib),
Err(err) => last_error = Some(err),
}
}
for name in names {
let realpath = match prefix {
Some(prefix) => Path::new(prefix).join(name),
None => PathBuf::from(name),
};
match libloading::Library::new(realpath) {
Ok(lib) => return Ok(lib),
Err(err) => last_error = Some(err),
}
}
Err(last_error.unwrap())
}
macro_rules! xcb_get_funcs {
($($name:ident),*) => {
let lib = crate::ffi::XcbLib::open()?;
$(
let $name = lib.$name;
)*
};
}
pub(crate) use xcb_get_funcs;
}
#[cfg(feature = "dl")]
pub(crate) use dl_impl::*;
#[cfg(feature = "dl")]
macro_rules! define_api_dynamic {
(
$(#[$smeta:meta])*
$svis:vis $sname:ident $cache:ident
libs: [$($lname:literal),+]
link: $link_name:literal
functions: $($(#[$meta:meta])* $fvis:vis fn $fname:ident($($farg:ident : $ftype:ty),*$(,)?) $(-> $fret:ty)?);+;
) => {
$(#[$smeta])*
#[derive(Clone)]
$svis struct $sname {
_lib: std::sync::Arc<libloading::Library>,
$(
$(#[$meta])*
$fvis $fname: unsafe extern "C" fn($($farg : $ftype),*) $(-> $fret)?
),+,
}
static $cache: std::sync::RwLock<Option<$sname>> = std::sync::RwLock::new(None);
impl $sname {
$svis fn open() -> Result<Self, crate::ffi::dl::OpenError> {
{
let lock = $cache.read()?;
if let Some(lib) = lock.as_ref() {
return Ok(lib.clone());
}
}
let mut lock = $cache.write()?;
if let Some(lib) = lock.as_ref() {
return Ok(lib.clone());
}
let lib = unsafe {
let lib = std::sync::Arc::new(crate::ffi::dl::load_library(None, &[$($lname),+])?);
Self {
$(
$fname: *lib.get(concat!(stringify!($fname), "\0").as_bytes())?
),+,
_lib: lib,
}
};
*lock = Some(lib.clone());
Ok(lib)
}
pub fn unload() -> Result<(), crate::ffi::dl::OpenError> {
let mut lock = $cache.write()?;
*lock = None;
Ok(())
}
}
};
}
#[cfg(feature = "dl")]
pub(crate) use define_api_dynamic;
#[cfg(feature = "dl")]
macro_rules! xcb_get_funcs_expect {
($($name:ident),*) => {
let lib = crate::ffi::XcbLib::open().expect("xcb library not loaded");
$(
let $name = lib.$name;
)*
};
}
#[cfg(feature = "dl")]
pub(crate) use xcb_get_funcs_expect;
#[cfg(not(feature = "dl"))]
macro_rules! define_api_link {
(
$(#[$_:meta])*
$svis:vis $sname:ident $cache:ident
libs: [$($lname:literal),+]
link: $link_name:literal
functions: $($(#[$meta:meta])* $fvis:vis fn $fname:ident($($farg:ident : $ftype:ty),*$(,)?) $(-> $fret:ty)?);+;
) => {
#[link(name = $link_name)]
extern "C" {
$(
$(#[$meta])*
$fvis fn $fname($($farg : $ftype),*) $(-> $fret)?;
)+
}
};
}
#[cfg(not(feature = "dl"))]
pub(crate) use define_api_link;