1 | use std::{ |
2 | os::unix::io::{AsRawFd, BorrowedFd, OwnedFd}, |
3 | sync::{Arc, Mutex}, |
4 | }; |
5 | |
6 | use super::{ |
7 | handle::State, ClientId, Data, GlobalHandler, GlobalId, Handle, InnerClientId, InnerGlobalId, |
8 | InnerHandle, InnerObjectId, ObjectId, |
9 | }; |
10 | use crate::{ |
11 | core_interfaces::{WL_DISPLAY_INTERFACE, WL_REGISTRY_INTERFACE}, |
12 | protocol::{same_interface, Argument, Message}, |
13 | rs::map::Object, |
14 | types::server::InitError, |
15 | }; |
16 | |
17 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
18 | use rustix::event::epoll; |
19 | |
20 | #[cfg (any( |
21 | target_os = "dragonfly" , |
22 | target_os = "freebsd" , |
23 | target_os = "netbsd" , |
24 | target_os = "openbsd" , |
25 | target_os = "macos" |
26 | ))] |
27 | use rustix::event::kqueue::*; |
28 | use smallvec::SmallVec; |
29 | |
30 | #[derive (Debug)] |
31 | pub struct InnerBackend<D: 'static> { |
32 | state: Arc<Mutex<State<D>>>, |
33 | } |
34 | |
35 | impl<D> InnerBackend<D> { |
36 | pub fn new() -> Result<Self, InitError> { |
37 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
38 | let poll_fd = epoll::create(epoll::CreateFlags::CLOEXEC) |
39 | .map_err(Into::into) |
40 | .map_err(InitError::Io)?; |
41 | |
42 | #[cfg (any( |
43 | target_os = "dragonfly" , |
44 | target_os = "freebsd" , |
45 | target_os = "netbsd" , |
46 | target_os = "openbsd" , |
47 | target_os = "macos" |
48 | ))] |
49 | let poll_fd = kqueue().map_err(Into::into).map_err(InitError::Io)?; |
50 | |
51 | Ok(Self { state: Arc::new(Mutex::new(State::new(poll_fd))) }) |
52 | } |
53 | |
54 | pub fn flush(&self, client: Option<ClientId>) -> std::io::Result<()> { |
55 | self.state.lock().unwrap().flush(client) |
56 | } |
57 | |
58 | pub fn handle(&self) -> Handle { |
59 | Handle { handle: InnerHandle { state: self.state.clone() as Arc<_> } } |
60 | } |
61 | |
62 | pub fn poll_fd(&self) -> BorrowedFd { |
63 | let raw_fd = self.state.lock().unwrap().poll_fd.as_raw_fd(); |
64 | // This allows the lifetime of the BorrowedFd to be tied to &self rather than the lock guard, |
65 | // which is the real safety concern |
66 | unsafe { BorrowedFd::borrow_raw(raw_fd) } |
67 | } |
68 | |
69 | pub fn dispatch_client( |
70 | &self, |
71 | data: &mut D, |
72 | client_id: InnerClientId, |
73 | ) -> std::io::Result<usize> { |
74 | let ret = self.dispatch_events_for(data, client_id); |
75 | let cleanup = self.state.lock().unwrap().cleanup(); |
76 | cleanup(&self.handle(), data); |
77 | ret |
78 | } |
79 | |
80 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
81 | pub fn dispatch_all_clients(&self, data: &mut D) -> std::io::Result<usize> { |
82 | use std::os::unix::io::AsFd; |
83 | |
84 | let poll_fd = self.poll_fd(); |
85 | let mut dispatched = 0; |
86 | loop { |
87 | let mut events = epoll::EventVec::with_capacity(32); |
88 | epoll::wait(poll_fd.as_fd(), &mut events, 0)?; |
89 | |
90 | if events.is_empty() { |
91 | break; |
92 | } |
93 | |
94 | for event in events.iter() { |
95 | let id = InnerClientId::from_u64(event.data.u64()); |
96 | // remove the cb while we call it, to gracefully handle reentrancy |
97 | if let Ok(count) = self.dispatch_events_for(data, id) { |
98 | dispatched += count; |
99 | } |
100 | } |
101 | let cleanup = self.state.lock().unwrap().cleanup(); |
102 | cleanup(&self.handle(), data); |
103 | } |
104 | |
105 | Ok(dispatched) |
106 | } |
107 | |
108 | #[cfg (any( |
109 | target_os = "dragonfly" , |
110 | target_os = "freebsd" , |
111 | target_os = "netbsd" , |
112 | target_os = "openbsd" , |
113 | target_os = "macos" |
114 | ))] |
115 | pub fn dispatch_all_clients(&self, data: &mut D) -> std::io::Result<usize> { |
116 | use std::time::Duration; |
117 | |
118 | let poll_fd = self.poll_fd(); |
119 | let mut dispatched = 0; |
120 | loop { |
121 | let mut events = Vec::with_capacity(32); |
122 | let nevents = unsafe { kevent(&poll_fd, &[], &mut events, Some(Duration::ZERO))? }; |
123 | |
124 | if nevents == 0 { |
125 | break; |
126 | } |
127 | |
128 | for event in events.iter().take(nevents) { |
129 | let id = InnerClientId::from_u64(event.udata() as u64); |
130 | // remove the cb while we call it, to gracefully handle reentrancy |
131 | if let Ok(count) = self.dispatch_events_for(data, id) { |
132 | dispatched += count; |
133 | } |
134 | } |
135 | let cleanup = self.state.lock().unwrap().cleanup(); |
136 | cleanup(&self.handle(), data); |
137 | } |
138 | |
139 | Ok(dispatched) |
140 | } |
141 | |
142 | pub(crate) fn dispatch_events_for( |
143 | &self, |
144 | data: &mut D, |
145 | client_id: InnerClientId, |
146 | ) -> std::io::Result<usize> { |
147 | let mut dispatched = 0; |
148 | let handle = self.handle(); |
149 | let mut state = self.state.lock().unwrap(); |
150 | loop { |
151 | let action = { |
152 | let state = &mut *state; |
153 | if let Ok(client) = state.clients.get_client_mut(client_id.clone()) { |
154 | let (message, object) = match client.next_request() { |
155 | Ok(v) => v, |
156 | Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { |
157 | if dispatched > 0 { |
158 | break; |
159 | } else { |
160 | return Err(e); |
161 | } |
162 | } |
163 | Err(e) => { |
164 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
165 | { |
166 | epoll::delete(&state.poll_fd, client)?; |
167 | } |
168 | |
169 | #[cfg (any( |
170 | target_os = "dragonfly" , |
171 | target_os = "freebsd" , |
172 | target_os = "netbsd" , |
173 | target_os = "openbsd" , |
174 | target_os = "macos" |
175 | ))] |
176 | { |
177 | use rustix::event::kqueue::*; |
178 | use std::os::unix::io::{AsFd, AsRawFd}; |
179 | |
180 | let evt = Event::new( |
181 | EventFilter::Read(client.as_fd().as_raw_fd()), |
182 | EventFlags::DELETE, |
183 | client_id.as_u64() as isize, |
184 | ); |
185 | |
186 | let mut events = Vec::new(); |
187 | unsafe { |
188 | kevent(&state.poll_fd, &[evt], &mut events, None) |
189 | .map(|_| ())?; |
190 | } |
191 | } |
192 | return Err(e); |
193 | } |
194 | }; |
195 | dispatched += 1; |
196 | if same_interface(object.interface, &WL_DISPLAY_INTERFACE) { |
197 | client.handle_display_request(message, &mut state.registry); |
198 | continue; |
199 | } else if same_interface(object.interface, &WL_REGISTRY_INTERFACE) { |
200 | if let Some((client, global, object, handler)) = |
201 | client.handle_registry_request(message, &mut state.registry) |
202 | { |
203 | DispatchAction::Bind { client, global, object, handler } |
204 | } else { |
205 | continue; |
206 | } |
207 | } else { |
208 | let object_id = InnerObjectId { |
209 | id: message.sender_id, |
210 | serial: object.data.serial, |
211 | interface: object.interface, |
212 | client_id: client.id.clone(), |
213 | }; |
214 | let opcode = message.opcode; |
215 | let (arguments, is_destructor, created_id) = |
216 | match client.process_request(&object, message) { |
217 | Some(args) => args, |
218 | None => continue, |
219 | }; |
220 | // Return the whole set to invoke the callback while handle is not borrower via client |
221 | DispatchAction::Request { |
222 | object, |
223 | object_id, |
224 | opcode, |
225 | arguments, |
226 | is_destructor, |
227 | created_id, |
228 | } |
229 | } |
230 | } else { |
231 | return Err(std::io::Error::new( |
232 | std::io::ErrorKind::InvalidInput, |
233 | "Invalid client ID" , |
234 | )); |
235 | } |
236 | }; |
237 | match action { |
238 | DispatchAction::Request { |
239 | object, |
240 | object_id, |
241 | opcode, |
242 | arguments, |
243 | is_destructor, |
244 | created_id, |
245 | } => { |
246 | // temporarily unlock the state Mutex while this request is dispatched |
247 | std::mem::drop(state); |
248 | let ret = object.data.user_data.clone().request( |
249 | &handle.clone(), |
250 | data, |
251 | ClientId { id: client_id.clone() }, |
252 | Message { |
253 | sender_id: ObjectId { id: object_id.clone() }, |
254 | opcode, |
255 | args: arguments, |
256 | }, |
257 | ); |
258 | if is_destructor { |
259 | object.data.user_data.clone().destroyed( |
260 | &handle.clone(), |
261 | data, |
262 | ClientId { id: client_id.clone() }, |
263 | ObjectId { id: object_id.clone() }, |
264 | ); |
265 | } |
266 | // acquire the lock again and continue |
267 | state = self.state.lock().unwrap(); |
268 | if is_destructor { |
269 | if let Ok(client) = state.clients.get_client_mut(client_id.clone()) { |
270 | client.send_delete_id(object_id); |
271 | } |
272 | } |
273 | match (created_id, ret) { |
274 | (Some(child_id), Some(child_data)) => { |
275 | if let Ok(client) = state.clients.get_client_mut(client_id.clone()) { |
276 | client |
277 | .map |
278 | .with(child_id.id, |obj| obj.data.user_data = child_data) |
279 | .unwrap(); |
280 | } |
281 | } |
282 | (None, None) => {} |
283 | (Some(child_id), None) => { |
284 | // Allow the callback to not return any data if the client is already dead (typically |
285 | // if the callback provoked a protocol error) |
286 | if let Ok(client) = state.clients.get_client(client_id.clone()) { |
287 | if !client.killed { |
288 | panic!( |
289 | "Callback creating object {} did not provide any object data." , |
290 | child_id |
291 | ); |
292 | } |
293 | } |
294 | } |
295 | (None, Some(_)) => { |
296 | panic!("An object data was returned from a callback not creating any object" ); |
297 | } |
298 | } |
299 | // dropping the object calls destructors from which users could call into wayland-backend again. |
300 | // so lets release and relock the state again, to avoid a deadlock |
301 | std::mem::drop(state); |
302 | std::mem::drop(object); |
303 | state = self.state.lock().unwrap(); |
304 | } |
305 | DispatchAction::Bind { object, client, global, handler } => { |
306 | // temporarily unlock the state Mutex while this request is dispatched |
307 | std::mem::drop(state); |
308 | let child_data = handler.bind( |
309 | &handle.clone(), |
310 | data, |
311 | ClientId { id: client.clone() }, |
312 | GlobalId { id: global }, |
313 | ObjectId { id: object.clone() }, |
314 | ); |
315 | // acquire the lock again and continue |
316 | state = self.state.lock().unwrap(); |
317 | if let Ok(client) = state.clients.get_client_mut(client.clone()) { |
318 | client.map.with(object.id, |obj| obj.data.user_data = child_data).unwrap(); |
319 | } |
320 | } |
321 | } |
322 | } |
323 | Ok(dispatched) |
324 | } |
325 | } |
326 | |
327 | enum DispatchAction<D: 'static> { |
328 | Request { |
329 | object: Object<Data<D>>, |
330 | object_id: InnerObjectId, |
331 | opcode: u16, |
332 | arguments: SmallVec<[Argument<ObjectId, OwnedFd>; 4]>, |
333 | is_destructor: bool, |
334 | created_id: Option<InnerObjectId>, |
335 | }, |
336 | Bind { |
337 | object: InnerObjectId, |
338 | client: InnerClientId, |
339 | global: InnerGlobalId, |
340 | handler: Arc<dyn GlobalHandler<D>>, |
341 | }, |
342 | } |
343 | |