| 1 | // x11-rs: Rust bindings for X11 libraries |
| 2 | // The X11 libraries are available under the MIT license. |
| 3 | // These bindings are public domain. |
| 4 | |
| 5 | use std::ffi::{CStr, CString}; |
| 6 | use std::os::raw::{c_char, c_void}; |
| 7 | use std::path::Path; |
| 8 | |
| 9 | use super::error::{OpenError, OpenErrorKind}; |
| 10 | |
| 11 | include!(concat!(env!("OUT_DIR" ) , "/config.rs" )); |
| 12 | |
| 13 | // |
| 14 | // x11_link! |
| 15 | // |
| 16 | |
| 17 | macro_rules! x11_link { |
| 18 | { $struct_name:ident, $pkg_name:ident, [$($lib_name:expr),*], $nsyms:expr, |
| 19 | $(pub fn $fn_name:ident ($($param_name:ident : $param_type:ty),*) -> $ret_type:ty,)* |
| 20 | variadic: |
| 21 | $(pub fn $vfn_name:ident ($($vparam_name: ident : $vparam_type:ty),+) -> $vret_type:ty,)* |
| 22 | globals: |
| 23 | $(pub static $var_name:ident : $var_type:ty,)* |
| 24 | } => { |
| 25 | #[allow(clippy::manual_non_exhaustive)] |
| 26 | pub struct $struct_name { |
| 27 | _private: (), |
| 28 | $(pub $fn_name: unsafe extern "C" fn ($($param_type),*) -> $ret_type,)* |
| 29 | $(pub $vfn_name: unsafe extern "C" fn ($($vparam_type),+, ...) -> $vret_type,)* |
| 30 | $(pub $var_name: *mut $var_type,)* |
| 31 | } |
| 32 | |
| 33 | unsafe impl Send for $struct_name {} |
| 34 | unsafe impl Sync for $struct_name {} |
| 35 | |
| 36 | impl $struct_name { |
| 37 | pub fn open () -> Result<$struct_name, $crate::error::OpenError> { |
| 38 | /// Cached function pointers and global variables for X11 libraries. |
| 39 | static CACHED: once_cell::sync::OnceCell<($crate::link::DynamicLibrary, $struct_name)> = once_cell::sync::OnceCell::new(); |
| 40 | |
| 41 | // Use the cached library or open a new one. |
| 42 | let (_, funcs) = CACHED.get_or_try_init(|| { |
| 43 | unsafe { |
| 44 | let libdir = $crate::link::config::libdir::$pkg_name; |
| 45 | let lib = $crate::link::DynamicLibrary::open_multi(libdir, &[$($lib_name),*])?; |
| 46 | |
| 47 | // Load every function pointer. |
| 48 | let funcs = $struct_name { |
| 49 | _private: (), |
| 50 | $($fn_name: ::std::mem::transmute(lib.symbol(stringify!($fn_name))?),)* |
| 51 | $($vfn_name: ::std::mem::transmute(lib.symbol(stringify!($vfn_name))?),)* |
| 52 | $($var_name: ::std::mem::transmute(lib.symbol(stringify!($var_name))?),)* |
| 53 | }; |
| 54 | |
| 55 | Ok((lib, funcs)) |
| 56 | } |
| 57 | })?; |
| 58 | |
| 59 | Ok($struct_name { |
| 60 | _private: (), |
| 61 | $($fn_name: funcs.$fn_name,)* |
| 62 | $($vfn_name: funcs.$vfn_name,)* |
| 63 | $($var_name: funcs.$var_name,)* |
| 64 | }) |
| 65 | } |
| 66 | } |
| 67 | }; |
| 68 | } |
| 69 | |
| 70 | // |
| 71 | // DynamicLibrary |
| 72 | // |
| 73 | |
| 74 | pub struct DynamicLibrary { |
| 75 | handle: *mut c_void, |
| 76 | } |
| 77 | |
| 78 | impl DynamicLibrary { |
| 79 | pub fn open(name: &str) -> Result<DynamicLibrary, OpenError> { |
| 80 | unsafe { |
| 81 | let cname = match CString::new(name) { |
| 82 | Ok(cname) => cname, |
| 83 | Err(_) => { |
| 84 | return Err(OpenError::new( |
| 85 | OpenErrorKind::Library, |
| 86 | String::from("library name contains NUL byte(s)" ), |
| 87 | )); |
| 88 | } |
| 89 | }; |
| 90 | |
| 91 | let handle = libc::dlopen(cname.as_ptr(), libc::RTLD_LAZY); |
| 92 | |
| 93 | if handle.is_null() { |
| 94 | let msg = libc::dlerror(); |
| 95 | |
| 96 | if msg.is_null() { |
| 97 | return Err(OpenError::new(OpenErrorKind::Library, String::new())); |
| 98 | } |
| 99 | |
| 100 | let cmsg = CStr::from_ptr(msg as *const c_char); |
| 101 | let detail = cmsg.to_string_lossy().into_owned(); |
| 102 | return Err(OpenError::new(OpenErrorKind::Library, detail)); |
| 103 | } |
| 104 | |
| 105 | Ok(DynamicLibrary { |
| 106 | handle: handle as *mut c_void, |
| 107 | }) |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | pub fn open_multi( |
| 112 | libdir: Option<&'static str>, |
| 113 | names: &[&str], |
| 114 | ) -> Result<DynamicLibrary, OpenError> { |
| 115 | assert!(!names.is_empty()); |
| 116 | |
| 117 | let paths = libdir.map_or(Vec::new(), |dir| { |
| 118 | let path = Path::new(dir); |
| 119 | names |
| 120 | .iter() |
| 121 | .map(|name| path.join(name).to_str().unwrap().to_string()) |
| 122 | .collect::<Vec<_>>() |
| 123 | }); |
| 124 | |
| 125 | let mut msgs = Vec::new(); |
| 126 | |
| 127 | for name in names.iter().copied().chain(paths.iter().map(|x| &**x)) { |
| 128 | match DynamicLibrary::open(name) { |
| 129 | Ok(lib) => { |
| 130 | return Ok(lib); |
| 131 | } |
| 132 | Err(err) => { |
| 133 | msgs.push(format!(" {}" , err)); |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | let mut detail = String::new(); |
| 139 | |
| 140 | for (i, msg) in msgs.iter().enumerate() { |
| 141 | if i != 0 { |
| 142 | detail.push_str("; " ); |
| 143 | } |
| 144 | detail.push_str(msg.as_ref()); |
| 145 | } |
| 146 | |
| 147 | Err(OpenError::new(OpenErrorKind::Library, detail)) |
| 148 | } |
| 149 | |
| 150 | pub fn symbol(&self, name: &str) -> Result<*mut c_void, OpenError> { |
| 151 | unsafe { |
| 152 | let cname = match CString::new(name) { |
| 153 | Ok(cname) => cname, |
| 154 | Err(_) => { |
| 155 | return Err(OpenError::new( |
| 156 | OpenErrorKind::Symbol, |
| 157 | String::from("symbol name contains NUL byte(s)" ), |
| 158 | )); |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | let sym = libc::dlsym(self.handle as *mut _, cname.as_ptr()); |
| 163 | |
| 164 | if sym.is_null() { |
| 165 | let msg = libc::dlerror(); |
| 166 | |
| 167 | if msg.is_null() { |
| 168 | return Err(OpenError::new(OpenErrorKind::Symbol, String::from(name))); |
| 169 | } |
| 170 | |
| 171 | let cmsg = CStr::from_ptr(msg as *const c_char); |
| 172 | let detail = format!(" {} - {}" , name, cmsg.to_string_lossy().into_owned()); |
| 173 | return Err(OpenError::new(OpenErrorKind::Symbol, detail)); |
| 174 | } |
| 175 | |
| 176 | Ok(sym as *mut c_void) |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | impl Drop for DynamicLibrary { |
| 182 | fn drop (&mut self) { |
| 183 | unsafe { |
| 184 | libc::dlclose(self.handle as *mut _); |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | unsafe impl Send for DynamicLibrary {} |
| 190 | unsafe impl Sync for DynamicLibrary {} |
| 191 | |