wl_client/
ffi.rs

1//! libwayland FFI interface.
2//!
3//! This module contains the type definitions necessary to interact with libwayland.
4
5#![expect(non_camel_case_types)]
6
7use {
8    isnt::std_1::primitive::IsntConstPtrExt,
9    std::{
10        ffi::{CStr, c_char, c_int, c_void},
11        ptr,
12    },
13};
14
15#[cfg(test)]
16mod tests;
17
18/// The libwayland `wl_message` type.
19#[repr(C)]
20pub struct wl_message {
21    pub name: *const c_char,
22    pub signature: *const c_char,
23    pub types: *const *const wl_interface,
24}
25
26// SAFETY: pointers not implementing Sync is only a lint
27unsafe impl Sync for wl_message {}
28// SAFETY: pointers not implementing Send is only a lint
29unsafe impl Send for wl_message {}
30
31/// The libwayland `wl_interface` type.
32#[repr(C)]
33pub struct wl_interface {
34    pub name: *const c_char,
35    pub version: c_int,
36    pub method_count: c_int,
37    pub methods: *const wl_message,
38    pub event_count: c_int,
39    pub events: *const wl_message,
40}
41
42// SAFETY: pointers not implementing Send is only a lint
43unsafe impl Send for wl_interface {}
44// SAFETY: pointers not implementing Sync is only a lint
45unsafe impl Sync for wl_interface {}
46
47/// The libwayland `wl_array` type.
48#[repr(C)]
49pub struct wl_array {
50    pub size: usize,
51    pub alloc: usize,
52    pub data: *mut c_void,
53}
54
55// SAFETY: pointers not implementing Send is only a lint
56unsafe impl Send for wl_array {}
57// SAFETY: pointers not implementing Sync is only a lint
58unsafe impl Sync for wl_array {}
59
60/// The libwayland `wl_fixed_t` type.
61pub type wl_fixed_t = i32;
62
63pub(crate) type wl_dispatcher_func_t = unsafe extern "C" fn(
64    user_data: *const c_void,
65    target: *mut c_void,
66    opcode: u32,
67    msg: *const wl_message,
68    args: *mut wl_argument,
69) -> c_int;
70
71/// The libwayland `wl_object` type.
72///
73/// This is always a `wl_proxy` in the context of this crate.
74#[repr(C)]
75pub struct wl_object(());
76
77/// The libwayland `wl_argument` type.
78#[derive(Copy, Clone)]
79#[repr(C)]
80pub union wl_argument {
81    pub i: i32,
82    pub u: u32,
83    pub f: wl_fixed_t,
84    pub s: *const c_char,
85    pub o: *mut wl_object,
86    pub n: u32,
87    pub a: *mut wl_array,
88    pub h: i32,
89}
90
91// SAFETY: pointers not implementing Send is only a lint
92unsafe impl Send for wl_argument {}
93// SAFETY: pointers not implementing Sync is only a lint
94unsafe impl Sync for wl_argument {}
95
96/// The libwayland `wl_display` type.
97///
98/// This type morally has the following fields:
99///
100/// - `mutex: a mutex`
101#[repr(C)]
102pub struct wl_display(());
103
104/// The libwayland `wl_proxy` type.
105///
106/// This type morally has the following fields:
107///
108/// - `interface: *const wl_interface`
109/// - `display: *mut wl_display`,
110/// - `queue: *mut wl_event_queue`,
111/// - `user_data: *mut c_void`,
112/// - `dispatcher: *mut c_void`,
113/// - `dispatcher_data: *mut c_void`,
114///
115/// The `interface` and `display` fields are immutable.
116///
117/// The `queue` field is mutable and access is protected by the `wl_display.mutex`.
118///
119/// The `user_data`, `dispatcher`, and `dispatcher_data` fields are mutable and access is
120/// not synchronized.
121#[repr(C)]
122pub struct wl_proxy(());
123
124/// The libwayland `wl_event_display` type.
125///
126/// This type morally has the following fields:
127///
128/// - `events: Vec<..>`
129///
130/// where each event is an event that has been read from the socket but which has not yet
131/// been dispatched.
132///
133/// Access to `events` is protected by the `wl_display.mutex`.
134///
135/// The mutex is dropped before an event is dispatched.
136#[repr(C)]
137pub struct wl_event_queue(());
138
139/// Atomically destroys a proxy while sending a request.
140///
141/// If this is passed into wl_proxy_marshal_array_flags, the message is sent and the proxy
142/// is destroyed without dropping the display mutex.
143///
144/// This is required when sending a destructor message for a server-created object. Since
145/// otherwise the server might send a new object with the same ID before we have a chance
146/// to call wl_proxy_destroy. This would cause a fatal error in libwayland.
147pub(crate) const WL_MARSHAL_FLAG_DESTROY: u32 = 1 << 0;
148
149/// # Safety
150///
151/// `l` and `r` must be valid `wl_interface` definitions.
152pub(crate) unsafe fn interface_compatible(l: &wl_interface, r: &wl_interface) -> bool {
153    if ptr::eq(l, r) {
154        return true;
155    }
156    macro_rules! cmp_sig {
157        ($count:ident, $messages:ident) => {
158            if l.$count != r.$count {
159                return false;
160            }
161            for i in 0..l.$count as usize {
162                // SAFETY: This function requires that l and r are valid interface definitions.
163                let l_msg = unsafe { &*l.$messages.add(i) };
164                // SAFETY: Dito
165                let r_msg = unsafe { &*r.$messages.add(i) };
166                // SAFETY: Dito
167                let l_sig = unsafe { CStr::from_ptr(l_msg.signature) };
168                // SAFETY: Dito
169                let r_sig = unsafe { CStr::from_ptr(r_msg.signature) };
170                if l_sig != r_sig {
171                    return false;
172                }
173            }
174        };
175    }
176    cmp_sig!(method_count, methods);
177    cmp_sig!(event_count, events);
178    for i in 0..l.event_count as usize {
179        // SAFETY: This function requires that l and r are valid interface definitions.
180        let l_msg = unsafe { &*l.events.add(i) };
181        // SAFETY: Dito
182        let r_msg = unsafe { &*r.events.add(i) };
183        // SAFETY: Dito
184        let sig = unsafe { CStr::from_ptr(l_msg.signature) };
185        let mut idx = 0;
186        for &b in sig.to_bytes() {
187            if b == b'?' {
188                continue;
189            }
190            if b == b'o' || b == b'n' {
191                // SAFETY: Dito
192                let l_if = unsafe { *l_msg.types.add(idx as usize) };
193                // SAFETY: Dito
194                let r_if = unsafe { *r_msg.types.add(idx as usize) };
195                if l_if.is_null() != r_if.is_null() {
196                    return false;
197                }
198                if l_if.is_not_null() {
199                    // SAFETY: Dito
200                    let l_if = unsafe { &*l_if };
201                    // SAFETY: Dito
202                    let r_if = unsafe { &*r_if };
203                    // SAFETY: Dito
204                    if unsafe { !interface_compatible(l_if, r_if) } {
205                        return false;
206                    }
207                }
208            }
209            idx += 1;
210        }
211    }
212    true
213}