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