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::path::Path;
7 use std::os::raw::{ c_char, c_void };
8 
9 use libc;
10 
11 use error::{ OpenError, OpenErrorKind };
12 
13 include!(concat!(env!("OUT_DIR"), "/config.rs"));
14 
15 //
16 // x11_link!
17 //
18 
19 macro_rules! x11_link {
20   { $struct_name:ident, $pkg_name:ident, [$($lib_name:expr),*], $nsyms:expr,
21     $(pub fn $fn_name:ident ($($param_name:ident : $param_type:ty),*) -> $ret_type:ty,)*
22     variadic:
23     $(pub fn $vfn_name:ident ($($vparam_name: ident : $vparam_type:ty),+) -> $vret_type:ty,)*
24     globals:
25     $(pub static $var_name:ident : $var_type:ty,)*
26   } => {
27     pub struct $struct_name {
28       #[allow(dead_code)]
29       lib: ::link::DynamicLibrary,
30       $(pub $fn_name: unsafe extern "C" fn ($($param_type),*) -> $ret_type,)*
31       $(pub $vfn_name: unsafe extern "C" fn ($($vparam_type),+, ...) -> $vret_type,)*
32       $(pub $var_name: *mut $var_type,)*
33     }
34 
35     unsafe impl Send for $struct_name {}
36     unsafe impl Sync for $struct_name {}
37 
38     impl $struct_name {
39       unsafe fn init (this: *mut Self) -> Result<(), $crate::error::OpenError> {
40         lazy_static! {
41           static ref SYMS: [(&'static str, usize); $nsyms] = unsafe {[
42             $((stringify!($fn_name), &((*(0 as *const $struct_name)).$fn_name) as *const _ as usize),)*
43             $((stringify!($vfn_name), &((*(0 as *const $struct_name)).$vfn_name) as *const _ as usize),)*
44             $((stringify!($var_name), &((*(0 as *const $struct_name)).$var_name) as *const _ as usize),)*
45           ]};
46         }
47         let offset = this as usize;
48         for &(name, sym_offset) in SYMS.iter() {
49           *((offset + sym_offset) as *mut *mut _) = try!((*this).lib.symbol(name));
50         }
51         Ok(())
52       }
53 
54       pub fn open () -> Result<$struct_name, $crate::error::OpenError> {
55         unsafe {
56           let libdir = $crate::link::config::libdir::$pkg_name;
57           let lib = try!($crate::link::DynamicLibrary::open_multi(libdir, &[$($lib_name),*]));
58           let mut this: ::std::mem::ManuallyDrop<$struct_name>
59               = ::std::mem::uninitialized();
60           let this_ptr = &mut this as *mut _ as *mut $struct_name;
61           ::std::ptr::write(&mut (*this_ptr).lib, lib);
62           try!(Self::init(this_ptr));
63           Ok(::std::mem::ManuallyDrop::into_inner(this))
64         }
65       }
66     }
67   };
68 }
69 
70 
71 //
72 // DynamicLibrary
73 //
74 
75 
76 pub struct DynamicLibrary {
77   handle: *mut c_void,
78 }
79 
80 impl DynamicLibrary {
open(name: &str) -> Result<DynamicLibrary, OpenError>81   pub fn open (name: &str) -> Result<DynamicLibrary, OpenError> {
82     unsafe {
83       let cname = match CString::new(name) {
84         Ok(cname) => cname,
85         Err(_) => {
86           return Err(OpenError::new(OpenErrorKind::Library,
87                                     String::from("library name contains NUL byte(s)")));
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 { handle: handle as *mut c_void })
106     }
107   }
108 
open_multi(libdir: Option<&'static str>, names: &[&str]) -> Result<DynamicLibrary, OpenError>109   pub fn open_multi (libdir: Option<&'static str>, names: &[&str]) -> Result<DynamicLibrary, OpenError> {
110     assert!(!names.is_empty());
111 
112     let paths = libdir.map_or(Vec::new(), |dir| {
113         let path = Path::new(dir);
114         names.iter().map(|name| path.join(name).to_str().unwrap().to_string()).collect::<Vec<_>>()
115     });
116 
117     let mut msgs = Vec::new();
118 
119     for name in names.iter().map(|x| *x).chain(paths.iter().map(|x| &**x)) {
120       match DynamicLibrary::open(name) {
121         Ok(lib) => { return Ok(lib); },
122         Err(err) => { msgs.push(format!("{}", err)); },
123       }
124     }
125 
126     let mut detail = String::new();
127 
128     for i in 0..msgs.len() {
129       if i != 0 {
130         detail.push_str("; ");
131       }
132       detail.push_str(msgs[i].as_ref());
133     }
134 
135     Err(OpenError::new(OpenErrorKind::Library, detail))
136   }
137 
symbol(&self, name: &str) -> Result<*mut c_void, OpenError>138   pub fn symbol (&self, name: &str) -> Result<*mut c_void, OpenError> {
139     unsafe {
140       let cname = match CString::new(name) {
141         Ok(cname) => cname,
142         Err(_) => {
143           return Err(OpenError::new(OpenErrorKind::Symbol,
144                                     String::from("symbol name contains NUL byte(s)")));
145         },
146       };
147 
148       let sym = libc::dlsym(self.handle as *mut _, cname.as_ptr());
149 
150       if sym.is_null() {
151         let msg = libc::dlerror();
152 
153         if msg.is_null() {
154           return Err(OpenError::new(OpenErrorKind::Symbol, String::from(name)));
155         }
156 
157         let cmsg = CStr::from_ptr(msg as *const c_char);
158         let detail = format!("{} - {}", name, cmsg.to_string_lossy().into_owned());;
159         return Err(OpenError::new(OpenErrorKind::Symbol, detail));
160       }
161 
162       Ok(sym as *mut c_void)
163     }
164   }
165 }
166 
167 impl Drop for DynamicLibrary {
drop(&mut self)168   fn drop (&mut self) {
169     unsafe {
170       libc::dlclose(self.handle as *mut _);
171     }
172   }
173 }
174