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}