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