wl_proxy/
global_mapper.rs

1//! A filter for globals advertised via wl_registry.
2
3use {
4    crate::{
5        object::{Object, ObjectError},
6        protocols::{ObjectInterface, wayland::wl_registry::WlRegistry},
7    },
8    error_reporter::Report,
9    std::{collections::HashMap, rc::Rc},
10};
11
12#[cfg(test)]
13mod tests;
14
15/// A filter for globals advertised via wl_registry.
16///
17/// This type allows filtering globals sent by the server and advertising synthetic
18/// globals that are handled by the proxy.
19pub struct GlobalMapper {
20    server_to_client: HashMap<u32, Option<u32>>,
21    client_to_server: Vec<Option<u32>>,
22}
23
24impl Default for GlobalMapper {
25    fn default() -> Self {
26        let mut server_to_client = HashMap::new();
27        server_to_client.insert(0, None);
28        Self {
29            server_to_client,
30            client_to_server: vec![None],
31        }
32    }
33}
34
35trait RegistryApi {
36    fn bind(&self, name: u32, id: Rc<dyn Object>) -> Result<(), ObjectError>;
37    fn global(
38        &self,
39        name: u32,
40        interface: ObjectInterface,
41        version: u32,
42    ) -> Result<(), ObjectError>;
43    fn global_remove(&self, name: u32) -> Result<(), ObjectError>;
44}
45
46impl RegistryApi for WlRegistry {
47    fn bind(&self, name: u32, id: Rc<dyn Object>) -> Result<(), ObjectError> {
48        self.try_send_bind(name, id)
49    }
50
51    fn global(
52        &self,
53        name: u32,
54        interface: ObjectInterface,
55        version: u32,
56    ) -> Result<(), ObjectError> {
57        self.try_send_global(name, interface, version)
58    }
59
60    fn global_remove(&self, name: u32) -> Result<(), ObjectError> {
61        self.try_send_global_remove(name)
62    }
63}
64
65impl GlobalMapper {
66    /// Announces a synthetic global and returns the global name.
67    ///
68    /// This function is similar to [`GlobalMapper::try_add_synthetic_global`] but logs
69    /// a message instead of returning an error if the global could not be sent to the
70    /// client.
71    pub fn add_synthetic_global(
72        &mut self,
73        registry: &WlRegistry,
74        interface: ObjectInterface,
75        version: u32,
76    ) -> u32 {
77        self.add_synthetic_global_impl(registry, interface, version)
78    }
79
80    /// Tries to announce a synthetic global and returns the global name.
81    pub fn try_add_synthetic_global(
82        &mut self,
83        registry: &WlRegistry,
84        interface: ObjectInterface,
85        version: u32,
86    ) -> Result<u32, ObjectError> {
87        self.try_add_synthetic_global_impl(registry, interface, version)
88    }
89
90    /// Removes a synthetic global.
91    ///
92    /// This function is similar to [`GlobalMapper::try_remove_synthetic_global`] but logs
93    /// a message instead of returning an error if the global_remove event could not be
94    /// sent to the client.
95    pub fn remove_synthetic_global(&mut self, registry: &WlRegistry, name: u32) {
96        self.remove_synthetic_global_impl(registry, name);
97    }
98
99    /// Tries to remove a synthetic global.
100    pub fn try_remove_synthetic_global(
101        &mut self,
102        registry: &WlRegistry,
103        name: u32,
104    ) -> Result<(), ObjectError> {
105        self.try_remove_synthetic_global_impl(registry, name)
106    }
107
108    /// Handles a server-sent global event.
109    ///
110    /// This function is similar to [`GlobalMapper::try_forward_global`] but logs
111    /// a message instead of returning an error if the global could not be sent to the
112    /// client.
113    pub fn forward_global(
114        &mut self,
115        registry: &WlRegistry,
116        server_name: u32,
117        interface: ObjectInterface,
118        version: u32,
119    ) {
120        self.forward_global_impl(registry, server_name, interface, version)
121    }
122
123    /// Tries to handle a server-sent global event.
124    pub fn try_forward_global(
125        &mut self,
126        registry: &WlRegistry,
127        server_name: u32,
128        interface: ObjectInterface,
129        version: u32,
130    ) -> Result<(), ObjectError> {
131        self.try_forward_global_impl(registry, server_name, interface, version)
132    }
133
134    /// Ignores a server-sent global.
135    ///
136    /// This function should be used so that global_remove events can be filtered
137    /// properly.
138    pub fn ignore_global(&mut self, name: u32) {
139        self.server_to_client.insert(name, None);
140    }
141
142    /// Handles a server-sent global_remove event.
143    ///
144    /// This function is similar to [`GlobalMapper::try_forward_global_remove`] but
145    /// logs a message instead of returning an error if the event could not be sent to the
146    /// client.
147    pub fn forward_global_remove(&mut self, registry: &WlRegistry, server_name: u32) {
148        self.forward_global_remove_impl(registry, server_name);
149    }
150
151    /// Tries to handle a server-sent global_remove event.
152    pub fn try_forward_global_remove(
153        &mut self,
154        registry: &WlRegistry,
155        server_name: u32,
156    ) -> Result<(), ObjectError> {
157        self.try_forward_global_remove_impl(registry, server_name)
158    }
159
160    /// Handles a client-sent bind request.
161    ///
162    /// This function is similar to [`GlobalMapper::try_forward_bind`] but logs a
163    /// message instead of returning an error if the request could not be forwarded to the
164    /// server.
165    pub fn forward_bind(
166        &mut self,
167        registry: &WlRegistry,
168        client_name: u32,
169        object: &Rc<dyn Object>,
170    ) {
171        self.forward_bind_impl(registry, client_name, object);
172    }
173
174    /// Tries to handle a client-sent bind request.
175    pub fn try_forward_bind(
176        &mut self,
177        registry: &WlRegistry,
178        client_name: u32,
179        object: &Rc<dyn Object>,
180    ) -> Result<(), ObjectError> {
181        self.try_forward_bind_impl(registry, client_name, object)
182    }
183}
184
185impl GlobalMapper {
186    fn add_synthetic_global_impl(
187        &mut self,
188        registry: &impl RegistryApi,
189        interface: ObjectInterface,
190        version: u32,
191    ) -> u32 {
192        let (name, res) = self.add_synthetic_global_(registry, interface, version);
193        if let Err(e) = res {
194            log::warn!("Could not add synthetic global: {}", Report::new(e));
195        }
196        name
197    }
198
199    fn try_add_synthetic_global_impl(
200        &mut self,
201        registry: &impl RegistryApi,
202        interface: ObjectInterface,
203        version: u32,
204    ) -> Result<u32, ObjectError> {
205        let (name, res) = self.add_synthetic_global_(registry, interface, version);
206        res?;
207        Ok(name)
208    }
209
210    fn add_synthetic_global_(
211        &mut self,
212        registry: &impl RegistryApi,
213        interface: ObjectInterface,
214        version: u32,
215    ) -> (u32, Result<(), ObjectError>) {
216        let name = self.client_to_server.len() as u32;
217        self.client_to_server.push(None);
218        let res = registry.global(name, interface, version);
219        (name, res)
220    }
221
222    fn remove_synthetic_global_impl(&mut self, registry: &impl RegistryApi, name: u32) {
223        if let Err(e) = self.try_remove_synthetic_global_impl(registry, name) {
224            log::warn!("Could not remove synthetic global: {}", Report::new(e));
225        }
226    }
227
228    fn try_remove_synthetic_global_impl(
229        &mut self,
230        registry: &impl RegistryApi,
231        name: u32,
232    ) -> Result<(), ObjectError> {
233        registry.global_remove(name)
234    }
235
236    fn forward_global_impl(
237        &mut self,
238        registry: &impl RegistryApi,
239        server_name: u32,
240        interface: ObjectInterface,
241        version: u32,
242    ) {
243        if let Err(e) = self.try_forward_global_impl(registry, server_name, interface, version) {
244            log::warn!("Could not handle server global: {}", Report::new(e));
245        }
246    }
247
248    fn try_forward_global_impl(
249        &mut self,
250        registry: &impl RegistryApi,
251        server_name: u32,
252        interface: ObjectInterface,
253        version: u32,
254    ) -> Result<(), ObjectError> {
255        let client_name = self.client_to_server.len() as u32;
256        self.client_to_server.push(Some(server_name));
257        self.server_to_client.insert(server_name, Some(client_name));
258        registry.global(client_name, interface, version)
259    }
260
261    fn forward_global_remove_impl(&mut self, registry: &impl RegistryApi, server_name: u32) {
262        if let Err(e) = self.try_forward_global_remove_impl(registry, server_name) {
263            log::warn!("Could not handle server global remove: {}", Report::new(e));
264        }
265    }
266
267    fn try_forward_global_remove_impl(
268        &mut self,
269        registry: &impl RegistryApi,
270        server_name: u32,
271    ) -> Result<(), ObjectError> {
272        let Some(client_name) = self.server_to_client.remove(&server_name) else {
273            log::warn!(
274                "Server sent wl_registry.global_remove for name {server_name} but no such global exists"
275            );
276            return Ok(());
277        };
278        let Some(client_name) = client_name else {
279            return Ok(());
280        };
281        registry.global_remove(client_name)
282    }
283
284    fn forward_bind_impl(
285        &mut self,
286        registry: &impl RegistryApi,
287        client_name: u32,
288        object: &Rc<dyn Object>,
289    ) {
290        if let Err(e) = self.try_forward_bind_impl(registry, client_name, object) {
291            log::warn!("Could not handle client bind: {}", Report::new(e));
292        }
293    }
294
295    fn try_forward_bind_impl(
296        &mut self,
297        registry: &impl RegistryApi,
298        client_name: u32,
299        object: &Rc<dyn Object>,
300    ) -> Result<(), ObjectError> {
301        let Some(server_name) = self.client_to_server.get(client_name as usize) else {
302            log::warn!(
303                "Client sent wl_registry.bind for name {client_name} but not such global exists"
304            );
305            return Ok(());
306        };
307        let Some(server_name) = server_name else {
308            return Ok(());
309        };
310        registry.bind(*server_name, object.clone())
311    }
312}