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