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 | #[cfg (all(unix, not(any(target_os = "openbsd" , target_os = "netbsd" ))))] |
38 | const LIB_NAME: &str = "libxcb.so.1" ; |
39 | // libtool turns -version-info differently into SONAMES on Open and NetBSD. |
40 | // Also, the library is apparently not in the default search path, hence use a full path. |
41 | #[cfg (any(target_os = "openbsd" , target_os = "netbsd" ))] |
42 | const LIB_NAME: &str = "/usr/X11R7/lib/libxcb.so.2" ; |
43 | |
44 | let library = libloading::Library::new(LIB_NAME) |
45 | .map_err(|e| LibxcbLoadError::OpenLibError(LIB_NAME.into(), e.to_string()))?; |
46 | let funcs = LibxcbFuncs::new(&library).map_err(|(symbol, e)| { |
47 | LibxcbLoadError::GetSymbolError(symbol.into(), e.to_string()) |
48 | })?; |
49 | Ok(Self { |
50 | _library: library, |
51 | funcs, |
52 | }) |
53 | } |
54 | } |
55 | |
56 | use once_cell::sync::Lazy; |
57 | |
58 | static LIBXCB_LIBRARY: Lazy<Result<LibxcbLibrary, LibxcbLoadError>> = |
59 | Lazy::new(|| unsafe { LibxcbLibrary::load() }); |
60 | |
61 | pub(super) fn get_libxcb() -> &'static LibxcbLibrary { |
62 | #[cold ] |
63 | #[inline (never)] |
64 | fn failed(e: &LibxcbLoadError) -> ! { |
65 | panic!("failed to load libxcb: {}" , e); |
66 | } |
67 | match *LIBXCB_LIBRARY { |
68 | Ok(ref library) => library, |
69 | Err(ref e) => failed(e), |
70 | } |
71 | } |
72 | |
73 | /// Tries to dynamically load libxcb, returning an error on failure. |
74 | /// |
75 | /// It is not required to call this function, as libxcb will be lazily loaded. |
76 | /// However, if a lazy load fails, a panic will be raised, missing the chance |
77 | /// to (nicely) handle the error. |
78 | /// |
79 | /// It is safe to call this function more than once from the same or different |
80 | /// threads. Only the first call will try to load libxcb, subsequent calls will |
81 | /// always return the same result. |
82 | pub fn load_libxcb() -> Result<(), LibxcbLoadError> { |
83 | match Lazy::force(&LIBXCB_LIBRARY) { |
84 | Ok(_) => Ok(()), |
85 | Err(e) => Err(e.clone()), |
86 | } |
87 | } |
88 | } |
89 | |
90 | macro_rules! make_ffi_fn_defs { |
91 | { |
92 | $( |
93 | $(#[$fn_attr:meta])* |
94 | fn $fn_name:ident($($fn_arg_name:ident: $fn_arg_type:ty),*) $(-> $fn_ret_ty:ty)?; |
95 | )* |
96 | } => { |
97 | #[cfg(not(feature = "dl-libxcb" ))] |
98 | #[link(name = "xcb" )] |
99 | extern "C" { |
100 | $( |
101 | $(#[$fn_attr])* |
102 | pub(crate) fn $fn_name($($fn_arg_name: $fn_arg_type),*) $(-> $fn_ret_ty)?; |
103 | )* |
104 | } |
105 | |
106 | #[cfg(feature = "dl-libxcb" )] |
107 | struct LibxcbFuncs { |
108 | $( |
109 | $(#[$fn_attr])* |
110 | $fn_name: unsafe extern "C" fn($($fn_arg_name: $fn_arg_type),*) $(-> $fn_ret_ty)?, |
111 | )* |
112 | } |
113 | |
114 | #[cfg(feature = "dl-libxcb" )] |
115 | impl LibxcbFuncs { |
116 | unsafe fn new(library: &libloading::Library) -> Result<Self, (&'static [u8], libloading::Error)> { |
117 | Ok(Self { |
118 | $($fn_name: { |
119 | let symbol_name = concat!(stringify!($fn_name), " \0" ).as_bytes(); |
120 | *library.get(symbol_name).map_err(|e| (stringify!($fn_name).as_bytes(), e))? |
121 | },)* |
122 | }) |
123 | } |
124 | } |
125 | |
126 | $( |
127 | #[cfg(feature = "dl-libxcb" )] |
128 | $(#[$fn_attr])* |
129 | pub(crate) unsafe fn $fn_name($($fn_arg_name: $fn_arg_type),*) $(-> $fn_ret_ty)? { |
130 | (libxcb_library::get_libxcb().funcs.$fn_name)($($fn_arg_name),*) |
131 | } |
132 | )* |
133 | }; |
134 | } |
135 | |
136 | make_ffi_fn_defs! { |
137 | // From xcb.h |
138 | fn xcb_flush(c: *mut xcb_connection_t) -> c_int; |
139 | fn xcb_get_maximum_request_length(c: *mut xcb_connection_t) -> u32; |
140 | fn xcb_prefetch_maximum_request_length(c: *mut xcb_connection_t); |
141 | fn xcb_wait_for_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; |
142 | fn xcb_poll_for_event(c: *mut xcb_connection_t) -> *mut xcb_generic_event_t; |
143 | fn xcb_request_check( |
144 | c: *mut xcb_connection_t, |
145 | void_cookie: xcb_void_cookie_t |
146 | ) -> *mut xcb_generic_error_t; |
147 | fn xcb_discard_reply64(c: *mut xcb_connection_t, sequence: u64); |
148 | fn xcb_get_setup(c: *mut xcb_connection_t) -> *const xcb_setup_t; |
149 | #[cfg (unix)] |
150 | fn xcb_get_file_descriptor(c: *mut xcb_connection_t) -> c_int; |
151 | fn xcb_connection_has_error(c: *mut xcb_connection_t) -> c_int; |
152 | fn xcb_disconnect(c: *mut xcb_connection_t); |
153 | fn xcb_connect( |
154 | displayname: *const c_char, |
155 | screenp: *mut c_int |
156 | ) -> *mut xcb_connection_t; |
157 | fn xcb_generate_id(c: *mut xcb_connection_t) -> u32; |
158 | |
159 | // From xcbext.h |
160 | fn xcb_send_request64( |
161 | c: *mut xcb_connection_t, |
162 | flags: c_int, |
163 | vector: *mut iovec, |
164 | request: *const xcb_protocol_request_t |
165 | ) -> u64; |
166 | #[cfg (unix)] |
167 | fn xcb_send_request_with_fds64( |
168 | c: *mut xcb_connection_t, |
169 | flags: c_int, |
170 | vector: *mut iovec, |
171 | request: *const xcb_protocol_request_t, |
172 | num_fds: c_uint, |
173 | fds: *mut c_int |
174 | ) -> u64; |
175 | fn xcb_wait_for_reply64( |
176 | c: *mut xcb_connection_t, |
177 | request: u64, |
178 | e: *mut *mut xcb_generic_error_t |
179 | ) -> *mut c_void; |
180 | fn xcb_poll_for_reply64( |
181 | c: *mut xcb_connection_t, |
182 | request: u64, |
183 | reply: *mut *mut c_void, |
184 | error: *mut *mut xcb_generic_error_t |
185 | ) -> c_int; |
186 | } |
187 | |