miniquad_ply/native/
module.rs1#[derive(Debug, PartialEq, Eq, Clone)]
2pub enum Error {
3 DlOpenError(String),
4 DlSymError(String),
5}
6
7impl Display for Error {
8 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9 match self {
10 Self::DlOpenError(msg) => write!(f, "Shared library open error:\n{msg}"),
11 Self::DlSymError(msg) => write!(f, "Shared library symlink error:\n{msg}"),
12 }
13 }
14}
15
16#[cfg(any(target_os = "linux", target_os = "android"))]
17pub mod linux {
18 use super::Error;
19 use libc::{dlclose, dlopen, dlsym, RTLD_LAZY, RTLD_LOCAL};
20 use std::{
21 ffi::{c_void, CString},
22 ptr::NonNull,
23 };
24
25 pub struct Module(NonNull<c_void>);
26
27 impl Module {
28 pub fn load(path: &str) -> Result<Self, Error> {
29 let cpath = CString::new(path).unwrap();
30 let module = unsafe { dlopen(cpath.as_ptr(), RTLD_LAZY | RTLD_LOCAL) };
31 if module.is_null() {
32 Err(Error::DlOpenError(path.to_string()))
33 } else {
34 Ok(Module(unsafe { NonNull::new_unchecked(module) }))
35 }
36 }
37
38 pub fn get_symbol<F: Sized>(&self, name: &str) -> Result<F, Error> {
39 let cname = CString::new(name).unwrap();
40 let symbol = unsafe { dlsym(self.0.as_ptr(), cname.as_ptr()) };
41 if symbol.is_null() {
42 return Err(Error::DlSymError(name.to_string()));
43 }
44 Ok(unsafe { std::mem::transmute_copy::<_, F>(&symbol) })
45 }
46 }
47
48 impl Drop for Module {
49 fn drop(&mut self) {
50 unsafe { dlclose(self.0.as_ptr()) };
51 }
52 }
53}
54
55#[cfg(target_os = "windows")]
56mod windows {
57 use super::Error;
58 use winapi::{
59 shared::minwindef::HINSTANCE,
60 um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryA},
61 };
62
63 pub struct Module(pub HINSTANCE);
64
65 impl Module {
66 pub fn load(path: &str) -> Result<Self, Error> {
67 let cpath = std::ffi::CString::new(path).unwrap();
68 let library = unsafe { LoadLibraryA(cpath.as_ptr()) };
69 if library.is_null() {
70 return Err(Error::DlOpenError(path.to_string()));
71 }
72 Ok(Self(library))
73 }
74 pub fn get_symbol<F: Sized>(&self, name: &str) -> Result<F, Error> {
75 let cname = std::ffi::CString::new(name).unwrap();
76 let proc = unsafe { GetProcAddress(self.0, cname.as_ptr() as *const _) };
77 if proc.is_null() {
78 return Err(Error::DlSymError(name.to_string()));
79 }
80 Ok(unsafe { std::mem::transmute_copy(&proc) })
81 }
82 }
83
84 impl Drop for Module {
85 fn drop(&mut self) {
86 unsafe { FreeLibrary(self.0) };
87 }
88 }
89}
90
91use std::fmt::Display;
92
93#[cfg(any(target_os = "linux", target_os = "android"))]
94pub use linux::*;
95
96#[cfg(target_os = "windows")]
97pub use windows::Module;
98
99#[cfg(any(target_os = "linux", target_os = "android"))]
100#[macro_export]
101macro_rules! declare_module {
102 ($name:ident,
103 $path:literal$(, $fallback:literal)*$(,)?
104 ...
105 $($s_vis:vis $s_name:ident: $s_type:ty,)*
107 ...
108 $($f_vis:vis fn $f_name:ident($($f_arg:ty),*$(,)?)$( -> $f_ret:ty )?,)*
110 ...
111 $($v_vis:vis fn $v_name:ident($($v_arg:ty),*, ...)$( -> $v_ret:ty )?,)*
113 ...
114 $($vis:vis $field:ident: $field_ty:ty,)*) => {
116 #[derive(Clone)]
117 pub struct $name {
118 _module: std::rc::Rc<$crate::native::module::Module>,
119 $($s_vis $s_name: $s_type,)*
120 $($f_vis $f_name: unsafe extern "C" fn ($($f_arg),*)$( -> $f_ret)?,)*
121 $($v_vis $v_name: unsafe extern "C" fn ($($v_arg),*, ...)$( -> $v_ret)?,)*
122 $($vis $field: $field_ty)*
123 }
124 impl $name {
125 pub fn try_load() -> Result<Self, $crate::native::module::Error> {
126 $crate::native::module::Module::load($path)$(.or_else(|_| $crate::native::module::Module::load($fallback)))*
127 .map(|module|
128 $name {
129 $($s_name: module.get_symbol::<$s_type>(stringify!($s_name)).unwrap(),)*
130 $($f_name: module.get_symbol::<unsafe extern "C" fn ($($f_arg),*)$( -> $f_ret)?>(stringify!($f_name)).unwrap(),)*
131 $($v_name: module.get_symbol::<unsafe extern "C" fn ($($v_arg),*, ...)$( -> $v_ret)?>(stringify!($v_name)).unwrap(),)*
132 $($field: Default::default(),)*
133 _module: module.into(),
134 }
135 )
136 }
137 }
138 }
139}