x11_dl/
link.rs

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
5use std::ffi::{CStr, CString};
6use std::os::raw::{c_char, c_void};
7use std::path::Path;
8
9use super::error::{OpenError, OpenErrorKind};
10
11include!(concat!(env!("OUT_DIR"), "/config.rs"));
12
13//
14// x11_link!
15//
16
17macro_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
74pub struct DynamicLibrary {
75    handle: *mut c_void,
76}
77
78impl 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
181impl Drop for DynamicLibrary {
182  fn drop (&mut self) {
183    unsafe {
184      libc::dlclose(self.handle as *mut _);
185    }
186  }
187}
188
189unsafe impl Send for DynamicLibrary {}
190unsafe impl Sync for DynamicLibrary {}