1use std::mem;
2use std::os::unix::io::OwnedFd;
3use std::sync::MutexGuard;
4use std::sync::{
5 atomic::{AtomicI32, Ordering},
6 Arc, Mutex,
7};
8
9use wayland_client::{
10 globals::{BindError, GlobalList},
11 protocol::{
12 wl_callback, wl_compositor, wl_output, wl_region,
13 wl_surface::{self, WlSurface},
14 },
15 Connection, Dispatch, Proxy, QueueHandle, WEnum,
16};
17
18use crate::{
19 error::GlobalError,
20 globals::{GlobalData, ProvidesBoundGlobal},
21 output::{OutputData, OutputHandler, OutputState, ScaleWatcherHandle},
22};
23
24pub trait CompositorHandler: Sized {
25 /// The surface has either been moved into or out of an output and the output has a different scale factor.
26 fn scale_factor_changed(
27 &mut self,
28 conn: &Connection,
29 qh: &QueueHandle<Self>,
30 surface: &wl_surface::WlSurface,
31 new_factor: i32,
32 );
33
34 /// The surface has either been moved into or out of an output and the output has different transform.
35 fn transform_changed(
36 &mut self,
37 conn: &Connection,
38 qh: &QueueHandle<Self>,
39 surface: &wl_surface::WlSurface,
40 new_transform: wl_output::Transform,
41 );
42
43 /// A frame callback has been completed.
44 ///
45 /// Frame callbacks are used to avoid updating surfaces that are not currently visible. If a
46 /// frame callback is requested prior to committing a surface, the client should avoid drawing
47 /// to that surface until the callback completes. See the
48 /// [`WlSurface::frame`](wl_surface::WlSurface::frame) request for more details.
49 ///
50 /// This function will be called if you request a frame callback by passing the surface itself
51 /// as the userdata (`surface.frame(&queue, &surface)`); you can also implement [`Dispatch`]
52 /// for other values to more easily dispatch rendering for specific surface types.
53 fn frame(
54 &mut self,
55 conn: &Connection,
56 qh: &QueueHandle<Self>,
57 surface: &wl_surface::WlSurface,
58 time: u32,
59 );
60}
61
62pub trait SurfaceDataExt: Send + Sync {
63 fn surface_data(&self) -> &SurfaceData;
64}
65
66impl SurfaceDataExt for SurfaceData {
67 fn surface_data(&self) -> &SurfaceData {
68 self
69 }
70}
71
72#[derive(Debug)]
73pub struct CompositorState {
74 wl_compositor: wl_compositor::WlCompositor,
75}
76
77impl CompositorState {
78 /// The maximum API version for WlCompositor that this object will bind.
79 // Note: if bumping this version number, check if the changes to the wayland XML cause an API
80 // break in the rust interfaces. If it does, be sure to remove other ProvidesBoundGlobal
81 // impls; if it does not, consider adding one for the previous (compatible) version.
82 pub const API_VERSION_MAX: u32 = 6;
83
84 pub fn bind<State>(
85 globals: &GlobalList,
86 qh: &QueueHandle<State>,
87 ) -> Result<CompositorState, BindError>
88 where
89 State: Dispatch<wl_compositor::WlCompositor, GlobalData, State> + 'static,
90 {
91 let wl_compositor = globals.bind(qh, 1..=Self::API_VERSION_MAX, GlobalData)?;
92 Ok(CompositorState { wl_compositor })
93 }
94
95 pub fn wl_compositor(&self) -> &wl_compositor::WlCompositor {
96 &self.wl_compositor
97 }
98
99 pub fn create_surface<D>(&self, qh: &QueueHandle<D>) -> wl_surface::WlSurface
100 where
101 D: Dispatch<wl_surface::WlSurface, SurfaceData> + 'static,
102 {
103 self.create_surface_with_data(qh, Default::default())
104 }
105
106 pub fn create_surface_with_data<D, U>(
107 &self,
108 qh: &QueueHandle<D>,
109 data: U,
110 ) -> wl_surface::WlSurface
111 where
112 D: Dispatch<wl_surface::WlSurface, U> + 'static,
113 U: SurfaceDataExt + 'static,
114 {
115 self.wl_compositor.create_surface(qh, data)
116 }
117}
118
119/// Data associated with a [`WlSurface`](wl_surface::WlSurface).
120#[derive(Debug)]
121pub struct SurfaceData {
122 /// The scale factor of the output with the highest scale factor.
123 pub(crate) scale_factor: AtomicI32,
124
125 /// Parent surface used when creating subsurfaces.
126 ///
127 /// For top-level surfaces this is always `None`.
128 pub(crate) parent_surface: Option<WlSurface>,
129
130 /// The inner mutable storage.
131 inner: Mutex<SurfaceDataInner>,
132}
133
134impl SurfaceData {
135 /// Create a new surface that initially reports the given scale factor and parent.
136 pub fn new(parent_surface: Option<WlSurface>, scale_factor: i32) -> Self {
137 Self {
138 scale_factor: AtomicI32::new(scale_factor),
139 parent_surface,
140 inner: Default::default(),
141 }
142 }
143
144 /// The scale factor of the output with the highest scale factor.
145 pub fn scale_factor(&self) -> i32 {
146 self.scale_factor.load(Ordering::Relaxed)
147 }
148
149 /// The suggest transform for the surface.
150 pub fn transform(&self) -> wl_output::Transform {
151 self.inner.lock().unwrap().transform
152 }
153
154 /// The parent surface used for this surface.
155 ///
156 /// The surface is `Some` for primarily for subsurfaces,
157 /// since they must have a parent surface.
158 pub fn parent_surface(&self) -> Option<&WlSurface> {
159 self.parent_surface.as_ref()
160 }
161
162 /// The outputs the surface is currently inside.
163 pub fn outputs(&self) -> impl Iterator<Item = wl_output::WlOutput> {
164 self.inner.lock().unwrap().outputs.clone().into_iter()
165 }
166}
167
168impl Default for SurfaceData {
169 fn default() -> Self {
170 Self::new(parent_surface:None, scale_factor:1)
171 }
172}
173
174#[derive(Debug)]
175struct SurfaceDataInner {
176 /// The transform of the given surface.
177 transform: wl_output::Transform,
178
179 /// The outputs the surface is currently inside.
180 outputs: Vec<wl_output::WlOutput>,
181
182 /// A handle to the OutputInfo callback that dispatches scale updates.
183 watcher: Option<ScaleWatcherHandle>,
184}
185
186impl Default for SurfaceDataInner {
187 fn default() -> Self {
188 Self { transform: wl_output::Transform::Normal, outputs: Vec::new(), watcher: None }
189 }
190}
191
192/// An owned [`WlSurface`](wl_surface::WlSurface).
193///
194/// This destroys the surface on drop.
195#[derive(Debug)]
196pub struct Surface(wl_surface::WlSurface);
197
198impl Surface {
199 pub fn new<D>(
200 compositor: &impl ProvidesBoundGlobal<
201 wl_compositor::WlCompositor,
202 { CompositorState::API_VERSION_MAX },
203 >,
204 qh: &QueueHandle<D>,
205 ) -> Result<Self, GlobalError>
206 where
207 D: Dispatch<wl_surface::WlSurface, SurfaceData> + 'static,
208 {
209 Self::with_data(compositor, qh, Default::default())
210 }
211
212 pub fn with_data<D, U>(
213 compositor: &impl ProvidesBoundGlobal<
214 wl_compositor::WlCompositor,
215 { CompositorState::API_VERSION_MAX },
216 >,
217 qh: &QueueHandle<D>,
218 data: U,
219 ) -> Result<Self, GlobalError>
220 where
221 D: Dispatch<wl_surface::WlSurface, U> + 'static,
222 U: Send + Sync + 'static,
223 {
224 Ok(Surface(compositor.bound_global()?.create_surface(qh, data)))
225 }
226
227 pub fn wl_surface(&self) -> &wl_surface::WlSurface {
228 &self.0
229 }
230}
231
232impl From<wl_surface::WlSurface> for Surface {
233 fn from(surface: wl_surface::WlSurface) -> Self {
234 Surface(surface)
235 }
236}
237
238impl Drop for Surface {
239 fn drop(&mut self) {
240 self.0.destroy();
241 }
242}
243
244#[macro_export]
245macro_rules! delegate_compositor {
246 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
247 $crate::delegate_compositor!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; surface: []);
248 $crate::delegate_compositor!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; surface-only: $crate::compositor::SurfaceData);
249 };
250 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, surface: [$($surface: ty),*$(,)?]) => {
251 $crate::delegate_compositor!(@{ $(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty }; surface: [ $($surface),* ]);
252 };
253 (@{$($ty:tt)*}; surface: []) => {
254 $crate::reexports::client::delegate_dispatch!($($ty)*:
255 [
256 $crate::reexports::client::protocol::wl_compositor::WlCompositor: $crate::globals::GlobalData
257 ] => $crate::compositor::CompositorState
258 );
259 $crate::reexports::client::delegate_dispatch!($($ty)*:
260 [
261 $crate::reexports::client::protocol::wl_callback::WlCallback: $crate::reexports::client::protocol::wl_surface::WlSurface
262 ] => $crate::compositor::CompositorState
263 );
264 };
265 (@{$($ty:tt)*}; surface-only: $surface:ty) => {
266 $crate::reexports::client::delegate_dispatch!($($ty)*:
267 [
268 $crate::reexports::client::protocol::wl_surface::WlSurface: $surface
269 ] => $crate::compositor::CompositorState
270 );
271 };
272 (@$ty:tt; surface: [ $($surface:ty),+ ]) => {
273 $crate::delegate_compositor!(@$ty; surface: []);
274 $(
275 $crate::delegate_compositor!(@$ty; surface-only: $surface);
276 )*
277 };
278}
279
280impl<D, U> Dispatch<wl_surface::WlSurface, U, D> for CompositorState
281where
282 D: Dispatch<wl_surface::WlSurface, U> + CompositorHandler + OutputHandler + 'static,
283 U: SurfaceDataExt + 'static,
284{
285 fn event(
286 state: &mut D,
287 surface: &wl_surface::WlSurface,
288 event: wl_surface::Event,
289 data: &U,
290 conn: &Connection,
291 qh: &QueueHandle<D>,
292 ) {
293 let data = data.surface_data();
294 let mut inner = data.inner.lock().unwrap();
295
296 match event {
297 wl_surface::Event::Enter { output } => {
298 inner.outputs.push(output);
299 }
300 wl_surface::Event::Leave { output } => {
301 inner.outputs.retain(|o| o != &output);
302 }
303 wl_surface::Event::PreferredBufferScale { factor } => {
304 let current_scale = data.scale_factor.load(Ordering::Relaxed);
305 drop(inner);
306 data.scale_factor.store(factor, Ordering::Relaxed);
307 if current_scale != factor {
308 state.scale_factor_changed(conn, qh, surface, factor);
309 }
310 return;
311 }
312 wl_surface::Event::PreferredBufferTransform { transform } => {
313 // Only handle known values.
314 if let WEnum::Value(transform) = transform {
315 let old_transform = std::mem::replace(&mut inner.transform, transform);
316 drop(inner);
317 if old_transform != transform {
318 state.transform_changed(conn, qh, surface, transform);
319 }
320 }
321 return;
322 }
323 _ => unreachable!(),
324 }
325
326 // NOTE: with v6 we don't need any special handling of the scale factor, everything
327 // was handled from the above, so return.
328 if surface.version() >= 6 {
329 return;
330 }
331
332 inner.watcher.get_or_insert_with(|| {
333 // Avoid storing the WlSurface inside the closure as that would create a reference
334 // cycle. Instead, store the ID and re-create the proxy.
335 let id = surface.id();
336 OutputState::add_scale_watcher(state, move |state, conn, qh, _| {
337 let id = id.clone();
338 if let Ok(surface) = wl_surface::WlSurface::from_id(conn, id) {
339 if let Some(data) = surface.data::<U>() {
340 let data = data.surface_data();
341 let inner = data.inner.lock().unwrap();
342 dispatch_surface_state_updates(state, conn, qh, &surface, data, inner);
343 }
344 }
345 })
346 });
347
348 dispatch_surface_state_updates(state, conn, qh, surface, data, inner);
349 }
350}
351
352fn dispatch_surface_state_updates<D, U>(
353 state: &mut D,
354 conn: &Connection,
355 qh: &QueueHandle<D>,
356 surface: &WlSurface,
357 data: &SurfaceData,
358 mut inner: MutexGuard<SurfaceDataInner>,
359) where
360 D: Dispatch<wl_surface::WlSurface, U> + CompositorHandler + OutputHandler + 'static,
361 U: SurfaceDataExt + 'static,
362{
363 let current_scale = data.scale_factor.load(Ordering::Relaxed);
364 let (factor, transform) = match inner
365 .outputs
366 .iter()
367 .filter_map(|output| {
368 output
369 .data::<OutputData>()
370 .map(|data| data.with_output_info(|info| (info.scale_factor, info.transform)))
371 })
372 // NOTE: reduce will only work for more than 1 element, thus we map transform to normal
373 // since we can't guess which one to use. With the exactly one output, the corrent
374 // transform will be passed instead.
375 .reduce(|acc, props| (acc.0.max(props.0), wl_output::Transform::Normal))
376 {
377 None => return,
378 Some(props) => props,
379 };
380
381 data.scale_factor.store(factor, Ordering::Relaxed);
382 let old_transform = mem::replace(&mut inner.transform, transform);
383 // Drop the mutex before we send of any events.
384 drop(inner);
385
386 if factor != current_scale {
387 state.scale_factor_changed(conn, qh, surface, factor);
388 }
389
390 if transform != old_transform {
391 state.transform_changed(conn, qh, surface, transform);
392 }
393}
394
395/// A trivial wrapper around a [`WlRegion`][wl_region::WlRegion].
396///
397/// This destroys the region on drop.
398#[derive(Debug)]
399pub struct Region(wl_region::WlRegion);
400
401impl Region {
402 pub fn new(
403 compositor: &impl ProvidesBoundGlobal<
404 wl_compositor::WlCompositor,
405 { CompositorState::API_VERSION_MAX },
406 >,
407 ) -> Result<Region, GlobalError> {
408 compositor
409 .bound_global()
410 .map(|c| {
411 c.send_constructor(wl_compositor::Request::CreateRegion {}, Arc::new(RegionData))
412 .unwrap_or_else(|_| Proxy::inert(c.backend().clone()))
413 })
414 .map(Region)
415 }
416
417 pub fn add(&self, x: i32, y: i32, width: i32, height: i32) {
418 self.0.add(x, y, width, height)
419 }
420
421 pub fn subtract(&self, x: i32, y: i32, width: i32, height: i32) {
422 self.0.subtract(x, y, width, height)
423 }
424
425 pub fn wl_region(&self) -> &wl_region::WlRegion {
426 &self.0
427 }
428}
429
430impl Drop for Region {
431 fn drop(&mut self) {
432 self.0.destroy()
433 }
434}
435
436struct RegionData;
437
438impl wayland_client::backend::ObjectData for RegionData {
439 fn event(
440 self: Arc<Self>,
441 _: &wayland_client::backend::Backend,
442 _: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
443 ) -> Option<Arc<(dyn wayland_client::backend::ObjectData + 'static)>> {
444 unreachable!("wl_region has no events");
445 }
446 fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
447}
448
449impl<D> Dispatch<wl_compositor::WlCompositor, GlobalData, D> for CompositorState
450where
451 D: Dispatch<wl_compositor::WlCompositor, GlobalData> + CompositorHandler,
452{
453 fn event(
454 _: &mut D,
455 _: &wl_compositor::WlCompositor,
456 _: wl_compositor::Event,
457 _: &GlobalData,
458 _: &Connection,
459 _: &QueueHandle<D>,
460 ) {
461 unreachable!("wl_compositor has no events")
462 }
463}
464
465impl ProvidesBoundGlobal<wl_compositor::WlCompositor, { CompositorState::API_VERSION_MAX }>
466 for CompositorState
467{
468 fn bound_global(&self) -> Result<wl_compositor::WlCompositor, GlobalError> {
469 Ok(self.wl_compositor.clone())
470 }
471}
472
473impl<D> Dispatch<wl_callback::WlCallback, wl_surface::WlSurface, D> for CompositorState
474where
475 D: Dispatch<wl_callback::WlCallback, wl_surface::WlSurface> + CompositorHandler,
476{
477 fn event(
478 state: &mut D,
479 _: &wl_callback::WlCallback,
480 event: wl_callback::Event,
481 surface: &wl_surface::WlSurface,
482 conn: &Connection,
483 qh: &QueueHandle<D>,
484 ) {
485 match event {
486 wl_callback::Event::Done { callback_data: u32 } => {
487 state.frame(conn, qh, surface, time:callback_data);
488 }
489
490 _ => unreachable!(),
491 }
492 }
493}
494