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
10use 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")]
16pub(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
90macro_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
136make_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