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}