smithay_client_toolkit/
compositor.rs

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