1 | use crate::{ |
2 | backend_interface::*, |
3 | error::{InitError, SwResultExt}, |
4 | util, Rect, SoftBufferError, |
5 | }; |
6 | use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; |
7 | use std::{ |
8 | num::{NonZeroI32, NonZeroU32}, |
9 | sync::{Arc, Mutex}, |
10 | }; |
11 | use wayland_client::{ |
12 | backend::{Backend, ObjectId}, |
13 | globals::{registry_queue_init, GlobalListContents}, |
14 | protocol::{wl_registry, wl_shm, wl_surface}, |
15 | Connection, Dispatch, EventQueue, Proxy, QueueHandle, |
16 | }; |
17 | |
18 | mod buffer; |
19 | use buffer::WaylandBuffer; |
20 | |
21 | struct State; |
22 | |
23 | pub struct WaylandDisplayImpl<D: ?Sized> { |
24 | conn: Option<Connection>, |
25 | event_queue: Mutex<EventQueue<State>>, |
26 | qh: QueueHandle<State>, |
27 | shm: wl_shm::WlShm, |
28 | |
29 | /// The object that owns the display handle. |
30 | /// |
31 | /// This has to be dropped *after* the `conn` field, because the `conn` field implicitly borrows |
32 | /// this. |
33 | _display: D, |
34 | } |
35 | |
36 | impl<D: HasDisplayHandle + ?Sized> WaylandDisplayImpl<D> { |
37 | fn conn(&self) -> &Connection { |
38 | self.conn.as_ref().unwrap() |
39 | } |
40 | } |
41 | |
42 | impl<D: HasDisplayHandle + ?Sized> ContextInterface<D> for Arc<WaylandDisplayImpl<D>> { |
43 | fn new(display: D) -> Result<Self, InitError<D>> |
44 | where |
45 | D: Sized, |
46 | { |
47 | let raw = display.display_handle()?.as_raw(); |
48 | let wayland_handle = match raw { |
49 | RawDisplayHandle::Wayland(w) => w.display, |
50 | _ => return Err(InitError::Unsupported(display)), |
51 | }; |
52 | |
53 | let backend = unsafe { Backend::from_foreign_display(wayland_handle.as_ptr().cast()) }; |
54 | let conn = Connection::from_backend(backend); |
55 | let (globals, event_queue) = |
56 | registry_queue_init(&conn).swbuf_err("Failed to make round trip to server" )?; |
57 | let qh = event_queue.handle(); |
58 | let shm: wl_shm::WlShm = globals |
59 | .bind(&qh, 1..=1, ()) |
60 | .swbuf_err("Failed to instantiate Wayland Shm" )?; |
61 | Ok(Arc::new(WaylandDisplayImpl { |
62 | conn: Some(conn), |
63 | event_queue: Mutex::new(event_queue), |
64 | qh, |
65 | shm, |
66 | _display: display, |
67 | })) |
68 | } |
69 | } |
70 | |
71 | impl<D: ?Sized> Drop for WaylandDisplayImpl<D> { |
72 | fn drop(&mut self) { |
73 | // Make sure the connection is dropped first. |
74 | self.conn = None; |
75 | } |
76 | } |
77 | |
78 | pub struct WaylandImpl<D: ?Sized, W: ?Sized> { |
79 | display: Arc<WaylandDisplayImpl<D>>, |
80 | surface: Option<wl_surface::WlSurface>, |
81 | buffers: Option<(WaylandBuffer, WaylandBuffer)>, |
82 | size: Option<(NonZeroI32, NonZeroI32)>, |
83 | |
84 | /// The pointer to the window object. |
85 | /// |
86 | /// This has to be dropped *after* the `surface` field, because the `surface` field implicitly |
87 | /// borrows this. |
88 | window_handle: W, |
89 | } |
90 | |
91 | impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> WaylandImpl<D, W> { |
92 | fn surface(&self) -> &wl_surface::WlSurface { |
93 | self.surface.as_ref().unwrap() |
94 | } |
95 | |
96 | fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { |
97 | let _ = self |
98 | .display |
99 | .event_queue |
100 | .lock() |
101 | .unwrap_or_else(|x| x.into_inner()) |
102 | .dispatch_pending(&mut State); |
103 | |
104 | if let Some((front, back)) = &mut self.buffers { |
105 | // Swap front and back buffer |
106 | std::mem::swap(front, back); |
107 | |
108 | front.age = 1; |
109 | if back.age != 0 { |
110 | back.age += 1; |
111 | } |
112 | |
113 | front.attach(self.surface.as_ref().unwrap()); |
114 | |
115 | // Like Mesa's EGL/WSI implementation, we damage the whole buffer with `i32::MAX` if |
116 | // the compositor doesn't support `damage_buffer`. |
117 | // https://bugs.freedesktop.org/show_bug.cgi?id=78190 |
118 | if self.surface().version() < 4 { |
119 | self.surface().damage(0, 0, i32::MAX, i32::MAX); |
120 | } else { |
121 | for rect in damage { |
122 | // Introduced in version 4, it is an error to use this request in version 3 or lower. |
123 | let (x, y, width, height) = (|| { |
124 | Some(( |
125 | i32::try_from(rect.x).ok()?, |
126 | i32::try_from(rect.y).ok()?, |
127 | i32::try_from(rect.width.get()).ok()?, |
128 | i32::try_from(rect.height.get()).ok()?, |
129 | )) |
130 | })() |
131 | .ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?; |
132 | self.surface().damage_buffer(x, y, width, height); |
133 | } |
134 | } |
135 | |
136 | self.surface().commit(); |
137 | } |
138 | |
139 | let _ = self |
140 | .display |
141 | .event_queue |
142 | .lock() |
143 | .unwrap_or_else(|x| x.into_inner()) |
144 | .flush(); |
145 | |
146 | Ok(()) |
147 | } |
148 | } |
149 | |
150 | impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> |
151 | for WaylandImpl<D, W> |
152 | { |
153 | type Context = Arc<WaylandDisplayImpl<D>>; |
154 | type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; |
155 | |
156 | fn new(window: W, display: &Arc<WaylandDisplayImpl<D>>) -> Result<Self, InitError<W>> { |
157 | // Get the raw Wayland window. |
158 | let raw = window.window_handle()?.as_raw(); |
159 | let wayland_handle = match raw { |
160 | RawWindowHandle::Wayland(w) => w.surface, |
161 | _ => return Err(InitError::Unsupported(window)), |
162 | }; |
163 | |
164 | let surface_id = unsafe { |
165 | ObjectId::from_ptr( |
166 | wl_surface::WlSurface::interface(), |
167 | wayland_handle.as_ptr().cast(), |
168 | ) |
169 | } |
170 | .swbuf_err("Failed to create proxy for surface ID." )?; |
171 | let surface = wl_surface::WlSurface::from_id(display.conn(), surface_id) |
172 | .swbuf_err("Failed to create proxy for surface ID." )?; |
173 | Ok(Self { |
174 | display: display.clone(), |
175 | surface: Some(surface), |
176 | buffers: Default::default(), |
177 | size: None, |
178 | window_handle: window, |
179 | }) |
180 | } |
181 | |
182 | #[inline ] |
183 | fn window(&self) -> &W { |
184 | &self.window_handle |
185 | } |
186 | |
187 | fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { |
188 | self.size = Some( |
189 | (|| { |
190 | let width = NonZeroI32::try_from(width).ok()?; |
191 | let height = NonZeroI32::try_from(height).ok()?; |
192 | Some((width, height)) |
193 | })() |
194 | .ok_or(SoftBufferError::SizeOutOfRange { width, height })?, |
195 | ); |
196 | Ok(()) |
197 | } |
198 | |
199 | fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> { |
200 | let (width, height) = self |
201 | .size |
202 | .expect("Must set size of surface before calling `buffer_mut()`" ); |
203 | |
204 | if let Some((_front, back)) = &mut self.buffers { |
205 | // Block if back buffer not released yet |
206 | if !back.released() { |
207 | let mut event_queue = self |
208 | .display |
209 | .event_queue |
210 | .lock() |
211 | .unwrap_or_else(|x| x.into_inner()); |
212 | while !back.released() { |
213 | event_queue.blocking_dispatch(&mut State).map_err(|err| { |
214 | SoftBufferError::PlatformError( |
215 | Some("Wayland dispatch failure" .to_string()), |
216 | Some(Box::new(err)), |
217 | ) |
218 | })?; |
219 | } |
220 | } |
221 | |
222 | // Resize, if buffer isn't large enough |
223 | back.resize(width.get(), height.get()); |
224 | } else { |
225 | // Allocate front and back buffer |
226 | self.buffers = Some(( |
227 | WaylandBuffer::new( |
228 | &self.display.shm, |
229 | width.get(), |
230 | height.get(), |
231 | &self.display.qh, |
232 | ), |
233 | WaylandBuffer::new( |
234 | &self.display.shm, |
235 | width.get(), |
236 | height.get(), |
237 | &self.display.qh, |
238 | ), |
239 | )); |
240 | }; |
241 | |
242 | let age = self.buffers.as_mut().unwrap().1.age; |
243 | Ok(BufferImpl { |
244 | stack: util::BorrowStack::new(self, |buffer| { |
245 | Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() }) |
246 | })?, |
247 | age, |
248 | }) |
249 | } |
250 | } |
251 | |
252 | impl<D: ?Sized, W: ?Sized> Drop for WaylandImpl<D, W> { |
253 | fn drop(&mut self) { |
254 | // Make sure the surface is dropped first. |
255 | self.surface = None; |
256 | } |
257 | } |
258 | |
259 | pub struct BufferImpl<'a, D: ?Sized, W> { |
260 | stack: util::BorrowStack<'a, WaylandImpl<D, W>, [u32]>, |
261 | age: u8, |
262 | } |
263 | |
264 | impl<'a, D: HasDisplayHandle + ?Sized, W: HasWindowHandle> BufferInterface |
265 | for BufferImpl<'a, D, W> |
266 | { |
267 | #[inline ] |
268 | fn pixels(&self) -> &[u32] { |
269 | self.stack.member() |
270 | } |
271 | |
272 | #[inline ] |
273 | fn pixels_mut(&mut self) -> &mut [u32] { |
274 | self.stack.member_mut() |
275 | } |
276 | |
277 | fn age(&self) -> u8 { |
278 | self.age |
279 | } |
280 | |
281 | fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { |
282 | self.stack.into_container().present_with_damage(damage) |
283 | } |
284 | |
285 | fn present(self) -> Result<(), SoftBufferError> { |
286 | let imp = self.stack.into_container(); |
287 | let (width, height) = imp |
288 | .size |
289 | .expect("Must set size of surface before calling `present()`" ); |
290 | imp.present_with_damage(&[Rect { |
291 | x: 0, |
292 | y: 0, |
293 | // We know width/height will be non-negative |
294 | width: width.try_into().unwrap(), |
295 | height: height.try_into().unwrap(), |
296 | }]) |
297 | } |
298 | } |
299 | |
300 | impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State { |
301 | fn event( |
302 | _: &mut State, |
303 | _: &wl_registry::WlRegistry, |
304 | _: wl_registry::Event, |
305 | _: &GlobalListContents, |
306 | _: &Connection, |
307 | _: &QueueHandle<State>, |
308 | ) { |
309 | // Ignore globals added after initialization |
310 | } |
311 | } |
312 | |
313 | impl Dispatch<wl_shm::WlShm, ()> for State { |
314 | fn event( |
315 | _: &mut State, |
316 | _: &wl_shm::WlShm, |
317 | _: wl_shm::Event, |
318 | _: &(), |
319 | _: &Connection, |
320 | _: &QueueHandle<State>, |
321 | ) { |
322 | } |
323 | } |
324 | |