| 1 | //! The code in this module allows to call libxcb's C functions. |
| 2 | //! |
| 3 | //! At its heart, this module only contains an `extern "C"` block that defines the C functions so |
| 4 | //! that they can be called from Rust. |
| 5 | //! |
| 6 | //! However, this module also contains the implementation of the `dl-libxcb` features. When this |
| 7 | //! future is enabled, we do not link against libxcb, but instead use `libloading` to load |
| 8 | //! `libxcb.so` at runtime. Most of the code is actually responsible for this later feature. |
| 9 | |
| 10 | use super::{ |
| 11 | c_char, c_int, c_uint, c_void, iovec, xcb_connection_t, xcb_generic_error_t, |
| 12 | xcb_generic_event_t, xcb_protocol_request_t, xcb_setup_t, xcb_void_cookie_t, |
| 13 | }; |
| 14 | |
| 15 | #[cfg (feature = "dl-libxcb" )] |
| 16 | pub(crate) mod libxcb_library { |
| 17 | use super::LibxcbFuncs; |
| 18 | use crate::errors::LibxcbLoadError; |
| 19 | |
| 20 | pub(super) struct LibxcbLibrary { |
| 21 | // Needed to keep the library loaded |
| 22 | _library: libloading::Library, |
| 23 | pub(super) funcs: LibxcbFuncs, |
| 24 | } |
| 25 | |
| 26 | impl LibxcbLibrary { |
| 27 | /// # Safety |
| 28 | /// |
| 29 | /// The functions pointers in `funcs` do not have lifetime, |
| 30 | /// but they must not outlive the returned result. |
| 31 | #[cold ] |
| 32 | #[inline (never)] |
| 33 | unsafe fn load() -> Result<Self, LibxcbLoadError> { |
| 34 | // TODO: Names for non-unix platforms |
| 35 | #[cfg (not(unix))] |
| 36 | compile_error!("dl-libxcb feature is not supported on non-unix" ); |
| 37 | |
| 38 | #[cfg (all(unix, target_os = "linux" ))] |
| 39 | const LIB_NAME: &str = "libxcb.so.1" ; |
| 40 | |
| 41 | // libtool turns -version-info differently into SONAMES on NetBSD. |
| 42 | // Also, the library is apparently not in the default search path, hence use a full path. |
| 43 | #[cfg (all(unix, target_os = "netbsd" ))] |
| 44 | const LIB_NAME: &str = "/usr/X11R7/lib/libxcb.so.2" ; |
| 45 | |
| 46 | // If we do not know anything, just assume libxcb.so and hope for the best. |
| 47 | // This is actually the right thing to do on OpenBSD since the dynamic linker then does |
| 48 | // some magic to find the right SONAME. |
| 49 | #[cfg (all(unix, not(any(target_os = "linux" , target_os = "netbsd" ))))] |
| 50 | const LIB_NAME: &str = "libxcb.so" ; |
| 51 | |
| 52 | let library = libloading::Library::new(LIB_NAME) |
| 53 | .map_err(|e| LibxcbLoadError::OpenLibError(LIB_NAME.into(), e.to_string()))?; |
| 54 | let funcs = LibxcbFuncs::new(&library).map_err(|(symbol, e)| { |
| 55 | LibxcbLoadError::GetSymbolError(symbol.into(), e.to_string()) |
| 56 | })?; |
| 57 | Ok(Self { |
| 58 | _library: library, |
| 59 | funcs, |
| 60 | }) |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | use once_cell::sync::Lazy; |
| 65 | |
| 66 | static LIBXCB_LIBRARY: Lazy<Result<LibxcbLibrary, LibxcbLoadError>> = |
| 67 | Lazy::new(|| unsafe { LibxcbLibrary::load() }); |
| 68 | |
| 69 | pub(super) fn get_libxcb() -> &'static LibxcbLibrary { |
| 70 | #[cold ] |
| 71 | #[inline (never)] |
| 72 | fn failed(e: &LibxcbLoadError) -> ! { |
| 73 | panic!("failed to load libxcb: {}" , e); |
| 74 | } |
| 75 | match *LIBXCB_LIBRARY { |
| 76 | Ok(ref library) => library, |
| 77 | Err(ref e) => failed(e), |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /// Tries to dynamically load libxcb, returning an error on failure. |
| 82 | /// |
| 83 | /// It is not required to call this function, as libxcb will be lazily loaded. |
| 84 | /// However, if a lazy load fails, a panic will be raised, missing the chance |
| 85 | /// to (nicely) handle the error. |
| 86 | /// |
| 87 | /// It is safe to call this function more than once from the same or different |
| 88 | /// threads. Only the first call will try to load libxcb, subsequent calls will |
| 89 | /// always return the same result. |
| 90 | pub fn load_libxcb() -> Result<(), LibxcbLoadError> { |
| 91 | match Lazy::force(&LIBXCB_LIBRARY) { |
| 92 | Ok(_) => Ok(()), |
| 93 | Err(e) => Err(e.clone()), |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | macro_rules! make_ffi_fn_defs { |
| 99 | { |
| 100 | $( |
| 101 | $(#[$fn_attr:meta])* |
| 102 | fn $fn_name:ident($($fn_arg_name:ident: $fn_arg_type:ty),*) $(-> $fn_ret_ty:ty)?; |
| 103 | )* |
| 104 | } => { |
| 105 | #[cfg(not(feature = "dl-libxcb" ))] |
| 106 | #[link(name = "xcb" )] |
| 107 | extern "C" { |
| 108 | $( |
| 109 | $(#[$fn_attr])* |
| 110 | pub(crate) fn $fn_name($($fn_arg_name: $fn_arg_type),*) $(-> $fn_ret_ty)?; |
| 111 | )* |
| 112 | } |
| 113 | |
| 114 | #[cfg(feature = "dl-libxcb" )] |
| 115 | struct LibxcbFuncs { |
| 116 | $( |
| 117 | $(#[$fn_attr])* |
| 118 | $fn_name: unsafe extern "C" fn($($fn_arg_name: $fn_arg_type),*) $(-> $fn_ret_ty)?, |
| 119 | )* |
| 120 | } |
| 121 | |
| 122 | #[cfg(feature = "dl-libxcb" )] |
| 123 | impl LibxcbFuncs { |
| 124 | unsafe fn new(library: &libloading::Library) -> Result<Self, (&'static [u8], libloading::Error)> { |
| 125 | Ok(Self { |
| 126 | $($fn_name: { |
| 127 | let symbol_name = concat!(stringify!($fn_name), " \0" ).as_bytes(); |
| 128 | *library.get(symbol_name).map_err(|e| (stringify!($fn_name).as_bytes(), e))? |
| 129 | },)* |
| 130 | }) |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | $( |
| 135 | #[cfg(feature = "dl-libxcb" )] |
| 136 | $(#[$fn_attr])* |
| 137 | pub(crate) unsafe fn $fn_name($($fn_arg_name: $fn_arg_type),*) $(-> $fn_ret_ty)? { |
| 138 | (libxcb_library::get_libxcb().funcs.$fn_name)($($fn_arg_name),*) |
| 139 | } |
| 140 | )* |
| 141 | }; |
| 142 | } |
| 143 | |
| 144 | make_ffi_fn_defs! { |
| 145 | // From xcb.h |
| 146 | fn xcb_flush(c: *mut xcb_connection_t) -> c_int; |
| 147 | fn xcb_get_maximum_request_length(c: *mut xcb_connection_t) -> u32; |
| 148 | fn xcb_prefetch_maximum_request_length(c: *mut xcb_connection_t); |
| 149 | fn xcb_wait_for_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; |
| 150 | fn xcb_poll_for_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; |
| 151 | fn xcb_request_check( |
| 152 | c: *mut xcb_connection_t, |
| 153 | void_cookie: xcb_void_cookie_t |
| 154 | ) -> *mut xcb_generic_error_t; |
| 155 | fn xcb_discard_reply64(c: *mut xcb_connection_t, sequence: u64); |
| 156 | fn xcb_get_setup(c: *mut xcb_connection_t) -> *const xcb_setup_t; |
| 157 | #[cfg (unix)] |
| 158 | fn xcb_get_file_descriptor(c: *mut xcb_connection_t) -> c_int; |
| 159 | fn xcb_connection_has_error(c: *mut xcb_connection_t) -> c_int; |
| 160 | fn xcb_disconnect(c: *mut xcb_connection_t); |
| 161 | fn xcb_connect( |
| 162 | displayname: *const c_char, |
| 163 | screenp: *mut c_int |
| 164 | ) -> *mut xcb_connection_t; |
| 165 | fn xcb_generate_id(c: *mut xcb_connection_t) -> u32; |
| 166 | |
| 167 | // From xcbext.h |
| 168 | fn xcb_send_request64( |
| 169 | c: *mut xcb_connection_t, |
| 170 | flags: c_int, |
| 171 | vector: *mut iovec, |
| 172 | request: *const xcb_protocol_request_t |
| 173 | ) -> u64; |
| 174 | #[cfg (unix)] |
| 175 | fn xcb_send_request_with_fds64( |
| 176 | c: *mut xcb_connection_t, |
| 177 | flags: c_int, |
| 178 | vector: *mut iovec, |
| 179 | request: *const xcb_protocol_request_t, |
| 180 | num_fds: c_uint, |
| 181 | fds: *mut c_int |
| 182 | ) -> u64; |
| 183 | fn xcb_wait_for_reply64( |
| 184 | c: *mut xcb_connection_t, |
| 185 | request: u64, |
| 186 | e: *mut *mut xcb_generic_error_t |
| 187 | ) -> *mut c_void; |
| 188 | fn xcb_poll_for_reply64( |
| 189 | c: *mut xcb_connection_t, |
| 190 | request: u64, |
| 191 | reply: *mut *mut c_void, |
| 192 | error: *mut *mut xcb_generic_error_t |
| 193 | ) -> c_int; |
| 194 | } |
| 195 | |