Skip to main content

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