1 | use std::{ |
2 | ffi::CString, |
3 | fmt, |
4 | os::unix::io::{BorrowedFd, OwnedFd}, |
5 | os::unix::{io::RawFd, net::UnixStream}, |
6 | sync::Arc, |
7 | }; |
8 | |
9 | use crate::protocol::{Interface, Message, ObjectInfo}; |
10 | pub use crate::types::server::{Credentials, DisconnectReason, GlobalInfo, InitError, InvalidId}; |
11 | |
12 | use super::server_impl; |
13 | |
14 | /// A trait representing your data associated to an object |
15 | /// |
16 | /// You will only be given access to it as a `&` reference, so you |
17 | /// need to handle interior mutability by yourself. |
18 | /// |
19 | /// The methods of this trait will be invoked internally every time a |
20 | /// new object is created to initialize its data. |
21 | pub trait ObjectData<D>: downcast_rs::DowncastSync { |
22 | /// Dispatch a request for the associated object |
23 | /// |
24 | /// If the request has a `NewId` argument, the callback must return the object data |
25 | /// for the newly created object |
26 | fn request( |
27 | self: Arc<Self>, |
28 | handle: &Handle, |
29 | data: &mut D, |
30 | client_id: ClientId, |
31 | msg: Message<ObjectId, OwnedFd>, |
32 | ) -> Option<Arc<dyn ObjectData<D>>>; |
33 | /// Notification that the object has been destroyed and is no longer active |
34 | fn destroyed( |
35 | self: Arc<Self>, |
36 | handle: &Handle, |
37 | data: &mut D, |
38 | client_id: ClientId, |
39 | object_id: ObjectId, |
40 | ); |
41 | /// Helper for forwarding a Debug implementation of your `ObjectData` type |
42 | /// |
43 | /// By default will just print `ObjectData { ... }` |
44 | #[cfg_attr (coverage, coverage(off))] |
45 | fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
46 | f.debug_struct("ObjectData" ).finish_non_exhaustive() |
47 | } |
48 | } |
49 | |
50 | downcast_rs::impl_downcast!(sync ObjectData<D>); |
51 | |
52 | impl<D: 'static> std::fmt::Debug for dyn ObjectData<D> { |
53 | #[cfg_attr (coverage, coverage(off))] |
54 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
55 | self.debug(f) |
56 | } |
57 | } |
58 | |
59 | /// A trait representing the handling of new bound globals |
60 | pub trait GlobalHandler<D>: downcast_rs::DowncastSync { |
61 | /// Check if given client is allowed to interact with given global |
62 | /// |
63 | /// If this function returns false, the client will not be notified of the existence |
64 | /// of this global, and any attempt to bind it will result in a protocol error as if |
65 | /// the global did not exist. |
66 | /// |
67 | /// Default implementation always return true. |
68 | fn can_view( |
69 | &self, |
70 | _client_id: ClientId, |
71 | _client_data: &Arc<dyn ClientData>, |
72 | _global_id: GlobalId, |
73 | ) -> bool { |
74 | true |
75 | } |
76 | /// A global has been bound |
77 | /// |
78 | /// Given client bound given global, creating given object. |
79 | /// |
80 | /// The method must return the object data for the newly created object. |
81 | fn bind( |
82 | self: Arc<Self>, |
83 | handle: &Handle, |
84 | data: &mut D, |
85 | client_id: ClientId, |
86 | global_id: GlobalId, |
87 | object_id: ObjectId, |
88 | ) -> Arc<dyn ObjectData<D>>; |
89 | /// Helper for forwarding a Debug implementation of your `GlobalHandler` type |
90 | /// |
91 | /// By default will just print `GlobalHandler { ... }` |
92 | #[cfg_attr (coverage, coverage(off))] |
93 | fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
94 | f.debug_struct("GlobalHandler" ).finish_non_exhaustive() |
95 | } |
96 | } |
97 | |
98 | impl<D: 'static> std::fmt::Debug for dyn GlobalHandler<D> { |
99 | #[cfg_attr (coverage, coverage(off))] |
100 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
101 | self.debug(f) |
102 | } |
103 | } |
104 | |
105 | downcast_rs::impl_downcast!(sync GlobalHandler<D>); |
106 | |
107 | /// A trait representing your data associated to a client |
108 | pub trait ClientData: downcast_rs::DowncastSync { |
109 | /// Notification that the client was initialized |
110 | fn initialized(&self, _client_id: ClientId) {} |
111 | /// Notification that the client is disconnected |
112 | fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {} |
113 | /// Helper for forwarding a Debug implementation of your `ClientData` type |
114 | /// |
115 | /// By default will just print `GlobalHandler { ... }` |
116 | #[cfg_attr (coverage, coverage(off))] |
117 | fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
118 | f.debug_struct(name:"ClientData" ).finish_non_exhaustive() |
119 | } |
120 | } |
121 | |
122 | impl std::fmt::Debug for dyn ClientData { |
123 | #[cfg_attr (coverage, coverage(off))] |
124 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
125 | self.debug(f) |
126 | } |
127 | } |
128 | |
129 | impl ClientData for () {} |
130 | |
131 | downcast_rs::impl_downcast!(sync ClientData); |
132 | |
133 | /// An ID representing a Wayland object |
134 | /// |
135 | /// The backend internally tracks which IDs are still valid, invalidates them when the protocol object they |
136 | /// represent is destroyed. As such even though the Wayland protocol reuses IDs, you still confidently compare |
137 | /// two `ObjectId` for equality, they will only compare as equal if they both represent the same protocol |
138 | /// object from the same client. |
139 | #[derive (Clone, PartialEq, Eq, Hash)] |
140 | pub struct ObjectId { |
141 | pub(crate) id: server_impl::InnerObjectId, |
142 | } |
143 | |
144 | impl ObjectId { |
145 | /// Returns whether this object is a null object. |
146 | /// |
147 | /// **Note:** This is not the same as checking if the ID is still valid, which cannot be done without the |
148 | /// [`Backend`]. A null ID is the ID equivalent of a null pointer: it never has been valid and never will |
149 | /// be. |
150 | pub fn is_null(&self) -> bool { |
151 | self.id.is_null() |
152 | } |
153 | |
154 | /// Returns an object id that represents a null object. |
155 | /// |
156 | /// This object ID is always invalid, and should be used for events with an optional `Object` argument. |
157 | #[inline ] |
158 | pub fn null() -> ObjectId { |
159 | server_impl::InnerHandle::null_id() |
160 | } |
161 | |
162 | /// Returns the interface of this object. |
163 | pub fn interface(&self) -> &'static Interface { |
164 | self.id.interface() |
165 | } |
166 | |
167 | /// Check if two object IDs are associated with the same client |
168 | /// |
169 | /// *Note:* This may spuriously return `false` if one (or both) of the objects to compare |
170 | /// is no longer valid. |
171 | pub fn same_client_as(&self, other: &Self) -> bool { |
172 | self.id.same_client_as(&other.id) |
173 | } |
174 | |
175 | /// Return the protocol-level numerical ID of this object |
176 | /// |
177 | /// Protocol IDs are reused after object destruction and each client has its own ID space, so this should |
178 | /// not be used as a unique identifier, instead use the `ObjectId` directly, it implements `Clone`, |
179 | /// `PartialEq`, `Eq` and `Hash`. |
180 | pub fn protocol_id(&self) -> u32 { |
181 | self.id.protocol_id() |
182 | } |
183 | } |
184 | |
185 | impl fmt::Display for ObjectId { |
186 | #[cfg_attr (coverage, coverage(off))] |
187 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
188 | self.id.fmt(f) |
189 | } |
190 | } |
191 | |
192 | impl fmt::Debug for ObjectId { |
193 | #[cfg_attr (coverage, coverage(off))] |
194 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
195 | self.id.fmt(f) |
196 | } |
197 | } |
198 | |
199 | /// An ID representing a Wayland client |
200 | /// |
201 | /// The backend internally tracks which IDs are still valid, invalidates them when the client they represent |
202 | /// is disconnected. As such you can confidently compare two `ClientId` for equality, they will only compare |
203 | /// as equal if they both represent the same client. |
204 | #[derive (Clone, PartialEq, Eq, Hash)] |
205 | pub struct ClientId { |
206 | pub(crate) id: server_impl::InnerClientId, |
207 | } |
208 | |
209 | impl fmt::Debug for ClientId { |
210 | #[cfg_attr (coverage, coverage(off))] |
211 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
212 | self.id.fmt(f) |
213 | } |
214 | } |
215 | |
216 | /// An Id representing a global |
217 | #[derive (Clone, PartialEq, Eq, Hash)] |
218 | pub struct GlobalId { |
219 | pub(crate) id: server_impl::InnerGlobalId, |
220 | } |
221 | |
222 | impl fmt::Debug for GlobalId { |
223 | #[cfg_attr (coverage, coverage(off))] |
224 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
225 | self.id.fmt(f) |
226 | } |
227 | } |
228 | |
229 | /// Main handle of a backend to the Wayland protocol |
230 | /// |
231 | /// This type hosts most of the protocol-related functionality of the backend, and is the |
232 | /// main entry point for manipulating Wayland objects. It can be retrieved from the backend via |
233 | /// [`Backend::handle()`](Backend::handle) and cloned, and is given to you as argument in many callbacks. |
234 | #[derive (Clone, Debug)] |
235 | pub struct Handle { |
236 | pub(crate) handle: server_impl::InnerHandle, |
237 | } |
238 | |
239 | /// A weak reference to a [`Handle`] |
240 | /// |
241 | /// This handle behaves similarly to [`Weak`](std::sync::Weak), and can be used to keep access to |
242 | /// the handle without actually preventing it from being dropped. |
243 | #[derive (Clone, Debug)] |
244 | pub struct WeakHandle { |
245 | pub(crate) handle: server_impl::WeakInnerHandle, |
246 | } |
247 | |
248 | impl WeakHandle { |
249 | /// Try to upgrade this weak handle to a [`Handle`] |
250 | /// |
251 | /// Returns `None` if the associated backend was already dropped. |
252 | pub fn upgrade(&self) -> Option<Handle> { |
253 | self.handle.upgrade().map(|handle: InnerHandle| Handle { handle }) |
254 | } |
255 | } |
256 | |
257 | impl Handle { |
258 | /// Get a [`WeakHandle`] from this handle |
259 | pub fn downgrade(&self) -> WeakHandle { |
260 | WeakHandle { handle: self.handle.downgrade() } |
261 | } |
262 | |
263 | /// Get the detailed protocol information about a wayland object |
264 | /// |
265 | /// Returns an error if the provided object ID is no longer valid. |
266 | #[inline ] |
267 | pub fn object_info(&self, id: ObjectId) -> Result<ObjectInfo, InvalidId> { |
268 | self.handle.object_info(id.id) |
269 | } |
270 | |
271 | /// Initializes a connection with a client. |
272 | /// |
273 | /// The `data` parameter contains data that will be associated with the client. |
274 | #[inline ] |
275 | pub fn insert_client( |
276 | &mut self, |
277 | stream: UnixStream, |
278 | data: Arc<dyn ClientData>, |
279 | ) -> std::io::Result<ClientId> { |
280 | Ok(ClientId { id: self.handle.insert_client(stream, data)? }) |
281 | } |
282 | |
283 | /// Returns the id of the client which owns the object. |
284 | #[inline ] |
285 | pub fn get_client(&self, id: ObjectId) -> Result<ClientId, InvalidId> { |
286 | self.handle.get_client(id.id) |
287 | } |
288 | |
289 | /// Returns the data associated with a client. |
290 | #[inline ] |
291 | pub fn get_client_data(&self, id: ClientId) -> Result<Arc<dyn ClientData>, InvalidId> { |
292 | self.handle.get_client_data(id.id) |
293 | } |
294 | |
295 | /// Retrive the [`Credentials`] of a client |
296 | #[inline ] |
297 | pub fn get_client_credentials(&self, id: ClientId) -> Result<Credentials, InvalidId> { |
298 | self.handle.get_client_credentials(id.id) |
299 | } |
300 | |
301 | /// Invokes a closure for all clients connected to this server |
302 | /// |
303 | /// Note that while this method is running, an internal lock of the backend is held, |
304 | /// as a result invoking other methods of the `Handle` within the closure will deadlock. |
305 | /// You should thus store the relevant `ClientId` in a container of your choice and process |
306 | /// them after this method has returned. |
307 | #[inline ] |
308 | pub fn with_all_clients(&self, f: impl FnMut(ClientId)) { |
309 | self.handle.with_all_clients(f) |
310 | } |
311 | |
312 | /// Invokes a closure for all objects owned by a client. |
313 | /// |
314 | /// Note that while this method is running, an internal lock of the backend is held, |
315 | /// as a result invoking other methods of the `Handle` within the closure will deadlock. |
316 | /// You should thus store the relevant `ObjectId` in a container of your choice and process |
317 | /// them after this method has returned. |
318 | #[inline ] |
319 | pub fn with_all_objects_for( |
320 | &self, |
321 | client_id: ClientId, |
322 | f: impl FnMut(ObjectId), |
323 | ) -> Result<(), InvalidId> { |
324 | self.handle.with_all_objects_for(client_id.id, f) |
325 | } |
326 | |
327 | /// Retrieve the `ObjectId` for a wayland object given its protocol numerical ID |
328 | #[inline ] |
329 | pub fn object_for_protocol_id( |
330 | &self, |
331 | client_id: ClientId, |
332 | interface: &'static Interface, |
333 | protocol_id: u32, |
334 | ) -> Result<ObjectId, InvalidId> { |
335 | self.handle.object_for_protocol_id(client_id.id, interface, protocol_id) |
336 | } |
337 | |
338 | /// Create a new object for given client |
339 | /// |
340 | /// To ensure state coherence of the protocol, the created object should be immediately |
341 | /// sent as a "New ID" argument in an event to the client. |
342 | /// |
343 | /// # Panics |
344 | /// |
345 | /// This method will panic if the type parameter `D` is not same to the same type as the |
346 | /// one the backend was initialized with. |
347 | #[inline ] |
348 | pub fn create_object<D: 'static>( |
349 | &self, |
350 | client_id: ClientId, |
351 | interface: &'static Interface, |
352 | version: u32, |
353 | data: Arc<dyn ObjectData<D>>, |
354 | ) -> Result<ObjectId, InvalidId> { |
355 | self.handle.create_object(client_id.id, interface, version, data) |
356 | } |
357 | |
358 | /// Send an event to the client |
359 | /// |
360 | /// Returns an error if the sender ID of the provided message is no longer valid. |
361 | /// |
362 | /// # Panics |
363 | /// |
364 | /// Checks against the protocol specification are done, and this method will panic if they do |
365 | /// not pass: |
366 | /// |
367 | /// - the message opcode must be valid for the sender interface |
368 | /// - the argument list must match the prototype for the message associated with this opcode |
369 | #[inline ] |
370 | pub fn send_event(&self, msg: Message<ObjectId, RawFd>) -> Result<(), InvalidId> { |
371 | self.handle.send_event(msg) |
372 | } |
373 | |
374 | /// Returns the data associated with an object. |
375 | /// |
376 | /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the |
377 | /// one the backend was initialized with. |
378 | #[inline ] |
379 | pub fn get_object_data<D: 'static>( |
380 | &self, |
381 | id: ObjectId, |
382 | ) -> Result<Arc<dyn ObjectData<D>>, InvalidId> { |
383 | self.handle.get_object_data(id.id) |
384 | } |
385 | |
386 | /// Returns the data associated with an object as a `dyn Any` |
387 | #[inline ] |
388 | pub fn get_object_data_any( |
389 | &self, |
390 | id: ObjectId, |
391 | ) -> Result<Arc<dyn std::any::Any + Send + Sync>, InvalidId> { |
392 | self.handle.get_object_data_any(id.id) |
393 | } |
394 | |
395 | /// Sets the data associated with some object. |
396 | /// |
397 | /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the |
398 | /// one the backend was initialized with. |
399 | #[inline ] |
400 | pub fn set_object_data<D: 'static>( |
401 | &self, |
402 | id: ObjectId, |
403 | data: Arc<dyn ObjectData<D>>, |
404 | ) -> Result<(), InvalidId> { |
405 | self.handle.set_object_data(id.id, data) |
406 | } |
407 | |
408 | /// Posts a protocol error on an object. This will also disconnect the client which created the object. |
409 | #[inline ] |
410 | pub fn post_error(&self, object_id: ObjectId, error_code: u32, message: CString) { |
411 | self.handle.post_error(object_id.id, error_code, message) |
412 | } |
413 | |
414 | /// Kills the connection to a client. |
415 | /// |
416 | /// The disconnection reason determines the error message that is sent to the client (if any). |
417 | #[inline ] |
418 | pub fn kill_client(&self, client_id: ClientId, reason: DisconnectReason) { |
419 | self.handle.kill_client(client_id.id, reason) |
420 | } |
421 | |
422 | /// Creates a global of the specified interface and version and then advertises it to clients. |
423 | /// |
424 | /// The clients which the global is advertised to is determined by the implementation of the [`GlobalHandler`]. |
425 | /// |
426 | /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the |
427 | /// one the backend was initialized with. |
428 | #[inline ] |
429 | pub fn create_global<D: 'static>( |
430 | &self, |
431 | interface: &'static Interface, |
432 | version: u32, |
433 | handler: Arc<dyn GlobalHandler<D>>, |
434 | ) -> GlobalId { |
435 | GlobalId { id: self.handle.create_global(interface, version, handler) } |
436 | } |
437 | |
438 | /// Disables a global object that is currently active. |
439 | /// |
440 | /// The global removal will be signaled to all currently connected clients. New clients will not know of |
441 | /// the global, but the associated state and callbacks will not be freed. As such, clients that still try |
442 | /// to bind the global afterwards (because they have not yet realized it was removed) will succeed. |
443 | /// |
444 | /// Invoking this method on an already disabled or removed global does nothing. It is not possible to |
445 | /// re-enable a disabled global, this method is meant to be invoked some time before actually removing |
446 | /// the global, to avoid killing clients because of a race. |
447 | /// |
448 | /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the |
449 | /// one the backend was initialized with. |
450 | #[inline ] |
451 | pub fn disable_global<D: 'static>(&self, id: GlobalId) { |
452 | self.handle.disable_global::<D>(id.id) |
453 | } |
454 | |
455 | /// Removes a global object and free its ressources. |
456 | /// |
457 | /// The global object will no longer be considered valid by the server, clients trying to bind it will be |
458 | /// killed, and the global ID is freed for re-use. |
459 | /// |
460 | /// It is advised to first disable a global and wait some amount of time before removing it, to ensure all |
461 | /// clients are correctly aware of its removal. Note that clients will generally not expect globals that |
462 | /// represent a capability of the server to be removed, as opposed to globals representing peripherals |
463 | /// (like `wl_output` or `wl_seat`). |
464 | /// |
465 | /// This methods does nothing if the provided `GlobalId` corresponds to an already removed global. |
466 | /// |
467 | /// **Panic:** This method will panic if the type parameter `D` is not same to the same type as the |
468 | /// one the backend was initialized with. |
469 | #[inline ] |
470 | pub fn remove_global<D: 'static>(&self, id: GlobalId) { |
471 | self.handle.remove_global::<D>(id.id) |
472 | } |
473 | |
474 | /// Returns information about a global. |
475 | #[inline ] |
476 | pub fn global_info(&self, id: GlobalId) -> Result<GlobalInfo, InvalidId> { |
477 | self.handle.global_info(id.id) |
478 | } |
479 | |
480 | /// Returns the handler which manages the visibility and notifies when a client has bound the global. |
481 | #[inline ] |
482 | pub fn get_global_handler<D: 'static>( |
483 | &self, |
484 | id: GlobalId, |
485 | ) -> Result<Arc<dyn GlobalHandler<D>>, InvalidId> { |
486 | self.handle.get_global_handler(id.id) |
487 | } |
488 | |
489 | /// Flushes pending events destined for a client. |
490 | /// |
491 | /// If no client is specified, all pending events are flushed to all clients. |
492 | pub fn flush(&mut self, client: Option<ClientId>) -> std::io::Result<()> { |
493 | self.handle.flush(client) |
494 | } |
495 | } |
496 | |
497 | /// A backend object that represents the state of a wayland server. |
498 | /// |
499 | /// A backend is used to drive a wayland server by receiving requests, dispatching messages to the appropriate |
500 | /// handlers and flushes requests to be sent back to the client. |
501 | #[derive (Debug)] |
502 | pub struct Backend<D: 'static> { |
503 | pub(crate) backend: server_impl::InnerBackend<D>, |
504 | } |
505 | |
506 | impl<D> Backend<D> { |
507 | /// Initialize a new Wayland backend |
508 | #[inline ] |
509 | pub fn new() -> Result<Self, InitError> { |
510 | Ok(Self { backend: server_impl::InnerBackend::new()? }) |
511 | } |
512 | |
513 | /// Flushes pending events destined for a client. |
514 | /// |
515 | /// If no client is specified, all pending events are flushed to all clients. |
516 | #[inline ] |
517 | pub fn flush(&mut self, client: Option<ClientId>) -> std::io::Result<()> { |
518 | self.backend.flush(client) |
519 | } |
520 | |
521 | /// Returns a handle which represents the server side state of the backend. |
522 | /// |
523 | /// The handle provides a variety of functionality, such as querying information about wayland objects, |
524 | /// obtaining data associated with a client and it's objects, and creating globals. |
525 | #[inline ] |
526 | pub fn handle(&self) -> Handle { |
527 | self.backend.handle() |
528 | } |
529 | |
530 | /// Returns the underlying file descriptor. |
531 | /// |
532 | /// The file descriptor may be monitored for activity with a polling mechanism such as epoll or kqueue. |
533 | /// When it becomes readable, this means there are pending messages that would be dispatched if you call |
534 | /// [`Backend::dispatch_all_clients`]. |
535 | /// |
536 | /// The file descriptor should not be used for any other purpose than monitoring it. |
537 | #[inline ] |
538 | pub fn poll_fd(&self) -> BorrowedFd { |
539 | self.backend.poll_fd() |
540 | } |
541 | |
542 | /// Dispatches all pending messages from the specified client. |
543 | /// |
544 | /// This method will not block if there are no pending messages. |
545 | /// |
546 | /// The provided `data` will be provided to the handler of messages received from the client. |
547 | /// |
548 | /// For performance reasons, use of this function should be integrated with an event loop, monitoring |
549 | /// the file descriptor associated with the client and only calling this method when messages are |
550 | /// available. |
551 | /// |
552 | /// **Note:** This functionality is currently only available on the rust backend, invoking this method on |
553 | /// the system backend will do the same as invoking |
554 | /// [`Backend::dispatch_all_clients()`](Backend::dispatch_all_clients). |
555 | #[inline ] |
556 | pub fn dispatch_single_client( |
557 | &mut self, |
558 | data: &mut D, |
559 | client_id: ClientId, |
560 | ) -> std::io::Result<usize> { |
561 | self.backend.dispatch_client(data, client_id.id) |
562 | } |
563 | |
564 | /// Dispatches all pending messages from all clients. |
565 | /// |
566 | /// This method will not block if there are no pending messages. |
567 | /// |
568 | /// The provided `data` will be provided to the handler of messages received from the clients. |
569 | /// |
570 | /// For performance reasons, use of this function should be integrated with an event loop, monitoring the |
571 | /// file descriptor retrieved by [`Backend::poll_fd`] and only calling this method when messages are |
572 | /// available. |
573 | #[inline ] |
574 | pub fn dispatch_all_clients(&mut self, data: &mut D) -> std::io::Result<usize> { |
575 | self.backend.dispatch_all_clients(data) |
576 | } |
577 | } |
578 | |
579 | pub(crate) struct DumbObjectData; |
580 | |
581 | impl<D> ObjectData<D> for DumbObjectData { |
582 | #[cfg_attr (coverage, coverage(off))] |
583 | fn request( |
584 | self: Arc<Self>, |
585 | _handle: &Handle, |
586 | _data: &mut D, |
587 | _client_id: ClientId, |
588 | _msg: Message<ObjectId, OwnedFd>, |
589 | ) -> Option<Arc<dyn ObjectData<D>>> { |
590 | unreachable!() |
591 | } |
592 | |
593 | #[cfg_attr (coverage, coverage(off))] |
594 | fn destroyed( |
595 | self: Arc<Self>, |
596 | _handle: &Handle, |
597 | _: &mut D, |
598 | _client_id: ClientId, |
599 | _object_id: ObjectId, |
600 | ) { |
601 | } |
602 | } |
603 | |