wl_client/libwayland.rs
1use {
2 crate::ffi::{
3 wl_argument, wl_dispatcher_func_t, wl_display, wl_event_queue, wl_interface, wl_proxy,
4 },
5 libloading::Library,
6 parking_lot::Mutex,
7 std::{
8 ffi::{c_char, c_int, c_void},
9 io, mem,
10 sync::LazyLock,
11 },
12};
13
14/// A reference to the `libwayland-client.so` dynamic library.
15///
16/// You can obtain a reference by calling [`Self::open`].
17pub struct Libwayland {
18 syms: Symbols,
19 syms_opt: SymbolsOpt,
20}
21
22/// # Safety
23///
24/// The types and names must match the libwayland ABI.
25macro_rules! unsafe_symbols {
26 (
27 $symbols:ident;
28 $(
29 $name:ident: $ty:ty,
30 )*
31 ) => {
32 struct $symbols {
33 $($name: $ty,)*
34 }
35
36 impl $symbols {
37 /// # Safety
38 ///
39 /// `lib` must be libwayland-client.so.
40 unsafe fn load(lib: &Library) -> Result<Self, libloading::Error> {
41 macro_rules! get {
42 ($v:ident: Option<$ty2:ty>) => {
43 $v.ok().map(|v| *v)
44 };
45 ($v:ident: $ty2:ty) => {
46 *$v?
47 };
48 }
49 Ok(Self {
50 $(
51 $name: {
52 // SAFETY: By the requirements of this macro, $name has the
53 // requested type.
54 let v = unsafe {
55 lib.get(concat!(stringify!($name), "\0").as_bytes())
56 };
57 get!(v: $ty)
58 },
59 )*
60 })
61 }
62 }
63 };
64}
65
66/// # Safety
67///
68/// - The types and names must match the libwayland API.
69/// - Each function must document the safety requirements of the libwayland function.
70macro_rules! unsafe_fwd {
71 (
72 $(
73 $(
74 #[$attr:meta]
75 )*
76 fn $name:ident($($arg:ident: $ty:ty),+$(,)?) $(-> $ret:ty)?;
77 )*
78 ) => {
79 // SAFETY: The requirement is forwarded to the caller.
80 unsafe_symbols! {
81 Symbols;
82 $(
83 $name: unsafe extern "C" fn($($arg: $ty,)*) $(-> $ret)?,
84 )*
85 }
86 impl Libwayland {
87 $(
88 $(
89 #[$attr]
90 )*
91 #[inline(always)]
92 pub(crate) unsafe fn $name(&self $(, $arg: $ty)*) $(-> $ret)? {
93 // SAFETY: - $name matches the API of libwayland
94 // - All safety requirements of the libwayland function are
95 // forwarded to the caller of this function.
96 unsafe {
97 (self.syms.$name)($($arg,)*)
98 }
99 }
100 )*
101 }
102 };
103}
104
105// SAFETY: There functions are as described in wayland-client-core.h.
106unsafe_symbols! {
107 SymbolsOpt;
108 wl_display_create_queue_with_name: Option<
109 unsafe extern "C" fn(display: *mut wl_display, name: *const c_char) -> *mut wl_event_queue,
110 >,
111 wl_proxy_get_queue: Option<unsafe extern "C" fn(proxy: *mut wl_proxy) -> *mut wl_event_queue>,
112 wl_proxy_get_display: Option<unsafe extern "C" fn(proxy: *mut wl_proxy) -> *mut wl_display>,
113}
114
115// SAFETY: - There functions are as described in wayland-client-core.h.
116// - The safety requirements are documented as far as I know them.
117unsafe_fwd! {
118 /// Destroys a queue.
119 ///
120 /// # Safety
121 ///
122 /// - queue must be a valid pointer
123 /// - all attached proxies must leak
124 fn wl_event_queue_destroy(queue: *mut wl_event_queue);
125
126 /// Sends a request.
127 ///
128 /// If flags contains WL_MARSHAL_FLAG_DESTROY, then this function destroys the proxy.
129 ///
130 /// If interface is not null, then this function returns a new non-wrapper proxy with
131 /// that interface.
132 ///
133 /// # Safety
134 ///
135 /// - proxy must be a valid pointer
136 /// - opcode must be a valid request opcode for the interface of the proxy
137 /// - args must be a pointer to an array of wl_arguments
138 /// - the array must conform to the interface + opcode of the proxy
139 /// - if interface is not null, it must be a valid pointer to a valid interface definition
140 /// - if interface is not null, then args must contain exactly on new_id element
141 /// - if interface is null, then args must not contain any new_id element
142 /// - flags must be 0 or WL_MARSHAL_FLAG_DESTROY
143 /// - if flags contains WL_MARSHAL_FLAG_DESTROY, then proxy must not be a wrapper
144 fn wl_proxy_marshal_array_flags(
145 proxy: *mut wl_proxy,
146 opcode: u32,
147 interface: *const wl_interface,
148 version: u32,
149 flags: u32,
150 args: *mut wl_argument,
151 ) -> *mut wl_proxy;
152
153 /// Creates a new wrapper proxy.
154 ///
155 /// # Safety
156 ///
157 /// - proxy must be a valid pointer.
158 fn wl_proxy_create_wrapper(proxy: *mut c_void) -> *mut c_void;
159
160 /// Destroys a wrapper proxy.
161 ///
162 /// # Safety
163 ///
164 /// - proxy must be a valid pointer to a wrapper
165 fn wl_proxy_wrapper_destroy(proxy: *mut c_void);
166
167 /// Destroys a non-wrapper proxy.
168 ///
169 /// # Safety
170 ///
171 /// - proxy must be a valid pointer to a wrapper
172 fn wl_proxy_destroy(proxy: *mut wl_proxy);
173
174 /// Sets the dispatcher of the proxy.
175 ///
176 /// # Safety
177 ///
178 /// - proxy must be a valid pointer to a non-wrapper
179 /// - this modifies the unprotected, mutable fields of the wl_proxy and access must be
180 /// externally synchronized
181 /// - the caller must ensure that the safety requirements of the dispatcher_func are
182 /// satisfied whenever it is called
183 fn wl_proxy_add_dispatcher(
184 proxy: *mut wl_proxy,
185 dispatcher_func: Option<wl_dispatcher_func_t>,
186 dispatcher_data: *const c_void,
187 data: *mut c_void,
188 );
189
190 /// Returns the ID of the proxy.
191 ///
192 /// # Safety
193 ///
194 /// - proxy must be a valid pointer
195 fn wl_proxy_get_id(proxy: *mut wl_proxy) -> u32;
196
197 /// Returns the version of the proxy.
198 ///
199 /// # Safety
200 ///
201 /// - proxy must be a valid pointer
202 fn wl_proxy_get_version(proxy: *mut wl_proxy) -> u32;
203
204 /// Sets the queue of the proxy.
205 ///
206 /// # Safety
207 ///
208 /// - proxy must be a valid pointer
209 /// - queue must be a valid pointer
210 /// - the queue must not be destroyed while it has proxies attached
211 /// - the queue and the proxy must belong to the same wl_display
212 fn wl_proxy_set_queue(proxy: *mut wl_proxy, queue: *mut wl_event_queue);
213
214 /// Connects to the wayland socket from the environment.
215 ///
216 /// # Safety
217 ///
218 /// - name must be null or a c string
219 fn wl_display_connect(name: *const c_char) -> *mut wl_display;
220
221 /// Connects to an existing file descriptor.
222 ///
223 /// # Safety
224 ///
225 /// - `fd` must be a valid file descriptor.
226 /// - This function takes ownership of the file descriptor.
227 fn wl_display_connect_to_fd(fd: c_int) -> *mut wl_display;
228
229 /// Disconnects the display.
230 ///
231 /// # Safety
232 ///
233 /// - display must be a valid pointer
234 /// - all queues and proxies must have been destroyed or must leak
235 fn wl_display_disconnect(display: *mut wl_display);
236
237 /// Dispatches a queue.
238 ///
239 /// # Safety
240 ///
241 /// - display and queue must be valid pointers
242 /// - the queue must belong to the display
243 /// - this accesses the unprotected, mutable fields of all proxies that were attached
244 /// to the queue before the start of the previous queue dispatch.
245 /// access must be externally synchronized.
246 fn wl_display_dispatch_queue_pending(
247 display: *mut wl_display,
248 queue: *mut wl_event_queue,
249 ) -> c_int;
250
251 /// Flushes the display.
252 ///
253 /// # Safety
254 ///
255 /// - display must be a valid pointer
256 fn wl_display_flush(display: *mut wl_display) -> c_int;
257
258 /// Creates a ticket to read from the display fd.
259 ///
260 /// # Safety
261 ///
262 /// - display must be a valid pointer
263 /// - queue must be a valid pointer
264 /// - the queue must belong to the display
265 fn wl_display_prepare_read_queue(
266 display: *mut wl_display,
267 queue: *mut wl_event_queue,
268 ) -> c_int;
269
270 /// Creates a ticket to read from the display fd.
271 ///
272 /// # Safety
273 ///
274 /// - display must be a valid pointer
275 fn wl_display_prepare_read(
276 display: *mut wl_display,
277 ) -> c_int;
278
279 /// Consumes a ticket to read from the display fd without reading from the display fd.
280 ///
281 /// # Safety
282 ///
283 /// - display must be a valid pointer
284 /// - the caller must own a ticket
285 fn wl_display_cancel_read(display: *mut wl_display);
286
287 /// Consumes a ticket to read from the display fd and reads from the display fd.
288 ///
289 /// # Safety
290 ///
291 /// - display must be a valid pointer
292 /// - the caller must own a ticket
293 fn wl_display_read_events(display: *mut wl_display) -> c_int;
294
295 /// Returns the file descriptor of the display.
296 ///
297 /// # Safety
298 ///
299 /// - display must be a valid pointer
300 fn wl_display_get_fd(display: *mut wl_display) -> c_int;
301
302 /// Creates a new queue.
303 ///
304 /// # Safety
305 ///
306 /// - display must be a valid pointer
307 fn wl_display_create_queue(display: *mut wl_display) -> *mut wl_event_queue;
308
309 /// Returns the errno of the last error that occurred.
310 ///
311 /// # Safety
312 ///
313 /// - display must be a valid pointer
314 fn wl_display_get_error(display: *mut wl_display) -> c_int;
315}
316
317impl Libwayland {
318 /// Obtains a reference to `libwayland-client.so`.
319 #[inline]
320 pub fn open() -> io::Result<&'static Self> {
321 static LIB: LazyLock<Result<Libwayland, Mutex<Option<libloading::Error>>>> =
322 LazyLock::new(|| Libwayland::open_new().map_err(|e| Mutex::new(Some(e))));
323 #[cold]
324 fn map_error(e: &Mutex<Option<libloading::Error>>) -> io::Error {
325 match e.lock().take() {
326 Some(e) => io::Error::new(io::ErrorKind::NotFound, e),
327 None => io::Error::from(io::ErrorKind::NotFound),
328 }
329 }
330 match &*LIB {
331 Ok(l) => Ok(l),
332 Err(e) => Err(map_error(e)),
333 }
334 }
335
336 #[cold]
337 fn open_new() -> Result<Self, libloading::Error> {
338 // SAFETY: No way to verify this. We just have to hope that libwayland-client.so
339 // is the libwayland-client.so that we've bound against.
340 let lib = unsafe { Library::new("libwayland-client.so.0")? };
341 // SAFETY: lib is libwayland-client.so.
342 let syms = unsafe { Symbols::load(&lib)? };
343 // SAFETY: lib is libwayland-client.so.
344 let syms_opt = unsafe { SymbolsOpt::load(&lib)? };
345 mem::forget(lib);
346 Ok(Libwayland { syms, syms_opt })
347 }
348
349 /// Creates a new queue.
350 ///
351 /// # Safety
352 ///
353 /// - display must be a valid pointer
354 /// - name must be null or a valid c string
355 pub(crate) unsafe fn wl_display_create_queue_with_name(
356 &self,
357 display: *mut wl_display,
358 name: *const c_char,
359 ) -> *mut wl_event_queue {
360 if let Some(f) = self.syms_opt.wl_display_create_queue_with_name {
361 // SAFETY: The requirements are forwarded to the caller of this function.
362 unsafe { f(display, name) }
363 } else {
364 // SAFETY: The requirements are forwarded to the caller of this function.
365 unsafe { self.wl_display_create_queue(display) }
366 }
367 }
368}
369
370mod polyfills {
371 use {
372 crate::{
373 Libwayland,
374 ffi::{wl_display, wl_event_queue, wl_interface, wl_proxy},
375 },
376 std::ffi::c_void,
377 };
378
379 #[repr(C)]
380 struct real_wl_object {
381 _interface: *const wl_interface,
382 _implementation: *const c_void,
383 _id: u32,
384 }
385
386 #[repr(C)]
387 struct real_wl_proxy {
388 _object: real_wl_object,
389 display: *mut wl_display,
390 queue: *mut wl_event_queue,
391 }
392
393 impl Libwayland {
394 /// Get the queue of a proxy.
395 ///
396 /// # Safety
397 ///
398 /// - proxy must be a valid pointer
399 #[inline]
400 pub(crate) unsafe fn wl_proxy_get_queue(
401 &self,
402 proxy: *mut wl_proxy,
403 ) -> *mut wl_event_queue {
404 if let Some(f) = self.syms_opt.wl_proxy_get_queue {
405 // SAFETY: The requirements are forwarded to the caller of this function.
406 return unsafe { f(proxy) };
407 }
408 let proxy = proxy.cast::<real_wl_proxy>();
409 // SAFETY: We have a hard dependency on wl_proxy_marshal_array_flags which was
410 // added in 2021. wl_proxy_get_queue was added in 2023. Between these
411 // two dates, the layout of wl_proxy has always been as described above.
412 // (Modulo trailing fields after the queue field.)
413 // NOTE: We cannot use this code for all versions of libwayland since the layout
414 // might change in the future.
415 unsafe { (*proxy).queue }
416 }
417
418 /// Get the display of a proxy.
419 ///
420 /// # Safety
421 ///
422 /// - proxy must be a valid pointer
423 #[inline]
424 pub(crate) unsafe fn wl_proxy_get_display(&self, proxy: *mut wl_proxy) -> *mut wl_display {
425 if let Some(f) = self.syms_opt.wl_proxy_get_display {
426 // SAFETY: The requirements are forwarded to the caller of this function.
427 return unsafe { f(proxy) };
428 }
429 let proxy = proxy.cast::<real_wl_proxy>();
430 // SAFETY: We have a hard dependency on wl_proxy_marshal_array_flags which was
431 // added in 2021. wl_proxy_get_display was added in 2023. Between these
432 // two dates, the layout of wl_proxy has always been as described above.
433 // (Modulo trailing fields after the queue field.)
434 // NOTE: We cannot use this code for all versions of libwayland since the layout
435 // might change in the future.
436 unsafe { (*proxy).display }
437 }
438 }
439}