🔒 Repository is read-only – file editing is disabled.

PaganDE/smithay-0.7.0/src/wayland/compositor/handlers.rs main

670 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Mutex,
};

use wayland_server::{
    protocol::{
        wl_callback::{self, WlCallback},
        wl_compositor::{self, WlCompositor},
        wl_region::{self, WlRegion},
        wl_subcompositor::{self, WlSubcompositor},
        wl_subsurface::{self, WlSubsurface},
        wl_surface::{self, WlSurface},
    },
    DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum,
};

use crate::utils::{
    alive_tracker::{AliveTracker, IsAlive},
    Client, Logical, Point,
};

use super::{
    cache::Cacheable,
    tree::{Location, PrivateSurfaceData},
    AlreadyHasRole, BufferAssignment, CompositorHandler, CompositorState, Damage, Rectangle, RectangleKind,
    RegionAttributes, SurfaceAttributes,
};

use tracing::trace;

/*
 * wl_compositor
 */

impl<D> GlobalDispatch<WlCompositor, (), D> for CompositorState
where
    D: GlobalDispatch<WlCompositor, ()>,
    D: Dispatch<WlCompositor, ()>,
    D: Dispatch<WlSurface, SurfaceUserData>,
    D: Dispatch<WlRegion, RegionUserData>,
    D: CompositorHandler,
    D: 'static,
{
    fn bind(
        _state: &mut D,
        _handle: &DisplayHandle,
        _client: &wayland_server::Client,
        resource: New<WlCompositor>,
        _global_data: &(),
        data_init: &mut DataInit<'_, D>,
    ) {
        data_init.init(resource, ());
    }
}

impl<D> Dispatch<WlCompositor, (), D> for CompositorState
where
    D: Dispatch<WlCompositor, ()>,
    D: Dispatch<WlSurface, SurfaceUserData>,
    D: Dispatch<WlRegion, RegionUserData>,
    D: CompositorHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &wayland_server::Client,
        _resource: &WlCompositor,
        request: wl_compositor::Request,
        _data: &(),
        _dhandle: &DisplayHandle,
        data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            wl_compositor::Request::CreateSurface { id } => {
                trace!(id = ?id, "Creating a new wl_surface");

                let surface = data_init.init(
                    id,
                    SurfaceUserData {
                        inner: PrivateSurfaceData::new(),
                        alive_tracker: Default::default(),
                        user_state_type: (std::any::TypeId::of::<D>(), std::any::type_name::<D>()),
                    },
                );

                state.compositor_state().surfaces.push(surface.clone());

                PrivateSurfaceData::init(&surface);
                state.new_surface(&surface);
            }
            wl_compositor::Request::CreateRegion { id } => {
                trace!(id = ?id, "Creating a new wl_region");

                data_init.init(
                    id,
                    RegionUserData {
                        inner: Default::default(),
                    },
                );
            }
            _ => unreachable!(),
        }
    }
}

/*
 * wl_surface
 */

impl Cacheable for SurfaceAttributes {
    fn commit(&mut self, _dh: &DisplayHandle) -> Self {
        SurfaceAttributes {
            buffer: self.buffer.take(),
            buffer_delta: self.buffer_delta.take(),
            buffer_scale: self.buffer_scale,
            buffer_transform: self.buffer_transform,
            damage: std::mem::take(&mut self.damage),
            opaque_region: self.opaque_region.clone(),
            input_region: self.input_region.clone(),
            frame_callbacks: std::mem::take(&mut self.frame_callbacks),
            client_scale: self.client_scale,
        }
    }
    fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
        if self.buffer.is_some() {
            if let Some(BufferAssignment::NewBuffer(buffer)) =
                std::mem::replace(&mut into.buffer, self.buffer)
            {
                let new_buffer = into.buffer.as_ref().and_then(|b| match b {
                    BufferAssignment::Removed => None,
                    BufferAssignment::NewBuffer(buffer) => Some(buffer),
                });

                if Some(&buffer) != new_buffer {
                    buffer.release();
                }
            }
        }
        into.buffer_delta = self.buffer_delta;
        into.buffer_scale = self.buffer_scale;
        into.buffer_transform = self.buffer_transform;
        into.damage.extend(self.damage);
        into.opaque_region = self.opaque_region;
        into.input_region = self.input_region;
        into.frame_callbacks.extend(self.frame_callbacks);
        into.client_scale = self.client_scale;
    }
}

/// User data for WlSurface
#[derive(Debug)]
pub struct SurfaceUserData {
    pub(crate) inner: Mutex<PrivateSurfaceData>,
    alive_tracker: AliveTracker,
    pub(super) user_state_type: (std::any::TypeId, &'static str),
}

impl<D> Dispatch<WlSurface, SurfaceUserData, D> for CompositorState
where
    D: Dispatch<WlSurface, SurfaceUserData>,
    D: Dispatch<WlCallback, ()>,
    D: CompositorHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        client: &wayland_server::Client,
        surface: &WlSurface,
        request: wl_surface::Request,
        _data: &SurfaceUserData,
        handle: &DisplayHandle,
        data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        match request {
            wl_surface::Request::Attach { buffer, x, y } => {
                let offset: Point<i32, Client> = (x, y).into();
                let offset = (x != 0 || y != 0).then_some(offset);

                // If version predates 5 just use the offset
                // Otherwise error out and use None
                let offset = if surface.version() < 5 {
                    offset
                } else {
                    if offset.is_some() {
                        surface.post_error(
                            wl_surface::Error::InvalidOffset,
                            "Passing non-zero x,y is protocol violation since versions 5",
                        );
                    }

                    None
                };

                let client_scale = state.client_compositor_state(client).client_scale();
                let offset = offset.map(|p| p.to_f64().to_logical(client_scale).to_i32_round());

                PrivateSurfaceData::with_states(surface, |states| {
                    let mut guard = states.cached_state.get::<SurfaceAttributes>();
                    let pending = guard.pending();

                    // Let's set the offset here in case it is supported and non-zero
                    if offset.is_some() {
                        pending.buffer_delta = offset;
                    }

                    pending.buffer = Some(match buffer {
                        Some(buffer) => BufferAssignment::NewBuffer(buffer),
                        None => BufferAssignment::Removed,
                    })
                });
            }
            wl_surface::Request::Damage { x, y, width, height } => {
                let client_scale = state.client_compositor_state(client).client_scale();
                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .damage
                        .push(Damage::Surface(
                            Rectangle::<i32, Client>::new((x, y).into(), (width, height).into())
                                .to_f64()
                                .to_logical(client_scale)
                                .to_i32_round(),
                        ));
                });
            }
            wl_surface::Request::Frame { callback } => {
                let callback = data_init.init(callback, ());

                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .frame_callbacks
                        .push(callback.clone());
                });
            }
            wl_surface::Request::SetOpaqueRegion { region } => {
                let attributes = region.map(|r| {
                    let attributes_mutex = &r.data::<RegionUserData>().unwrap().inner;
                    attributes_mutex.lock().unwrap().clone()
                });
                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .opaque_region = attributes;
                });
            }
            wl_surface::Request::SetInputRegion { region } => {
                let attributes = region.map(|r| {
                    let attributes_mutex = &r.data::<RegionUserData>().unwrap().inner;
                    attributes_mutex.lock().unwrap().clone()
                });
                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .input_region = attributes;
                });
            }
            wl_surface::Request::Commit => {
                let client_scale = state.client_compositor_state(client).client_scale();
                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .client_scale = client_scale;
                });

                PrivateSurfaceData::invoke_pre_commit_hooks(state, handle, surface);

                PrivateSurfaceData::commit(surface, handle, state);
            }
            wl_surface::Request::SetBufferTransform { transform } => {
                if let WEnum::Value(transform) = transform {
                    PrivateSurfaceData::with_states(surface, |states| {
                        states
                            .cached_state
                            .get::<SurfaceAttributes>()
                            .pending()
                            .buffer_transform = transform;
                    });
                }
            }
            wl_surface::Request::SetBufferScale { scale } => {
                if scale >= 1 {
                    PrivateSurfaceData::with_states(surface, |states| {
                        states
                            .cached_state
                            .get::<SurfaceAttributes>()
                            .pending()
                            .buffer_scale = scale;
                    });
                } else {
                    surface.post_error(wl_surface::Error::InvalidScale, "Scale must be positive");
                }
            }
            wl_surface::Request::DamageBuffer { x, y, width, height } => {
                if width < 0 || height < 0 {
                    return;
                }

                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .damage
                        .push(Damage::Buffer(Rectangle::new(
                            (x, y).into(),
                            (width, height).into(),
                        )))
                });
            }
            wl_surface::Request::Offset { x, y } => {
                let client_scale = state.client_compositor_state(client).client_scale();
                PrivateSurfaceData::with_states(surface, |states| {
                    states
                        .cached_state
                        .get::<SurfaceAttributes>()
                        .pending()
                        .buffer_delta = Some(
                        Point::<i32, Client>::from((x, y))
                            .to_f64()
                            .to_logical(client_scale)
                            .to_i32_round(),
                    );
                });
            }
            wl_surface::Request::Destroy => {
                // All is already handled by our destructor
            }
            _ => unreachable!(),
        }
    }

    fn destroyed(
        state: &mut D,
        _client_id: wayland_server::backend::ClientId,
        surface: &WlSurface,
        data: &SurfaceUserData,
    ) {
        // We let the destruction hooks run first and then tell the compositor handler the surface was
        // destroyed.
        data.alive_tracker.destroy_notify();
        state.destroyed(surface);

        // Remove the surface after the callback is invoked.
        state
            .compositor_state()
            .surfaces
            .retain(|s| s.id() != surface.id());
        PrivateSurfaceData::cleanup(state, data, surface);
    }
}

impl IsAlive for WlSurface {
    #[inline]
    fn alive(&self) -> bool {
        let data: &SurfaceUserData = self.data().unwrap();
        data.alive_tracker.alive()
    }
}

/*
 * wl_region
 */

/// User data of WlRegion
#[derive(Debug)]
pub struct RegionUserData {
    pub(crate) inner: Mutex<RegionAttributes>,
}

impl<D> Dispatch<WlRegion, RegionUserData, D> for CompositorState
where
    D: Dispatch<WlRegion, RegionUserData>,
    D: CompositorHandler,
{
    fn request(
        state: &mut D,
        client: &wayland_server::Client,
        _resource: &WlRegion,
        request: wl_region::Request,
        data: &RegionUserData,
        _dhandle: &DisplayHandle,
        _init: &mut wayland_server::DataInit<'_, D>,
    ) {
        let mut guard = data.inner.lock().unwrap();
        let client_scale = state.client_compositor_state(client).client_scale();

        match request {
            wl_region::Request::Add { x, y, width, height } => guard.rects.push((
                RectangleKind::Add,
                Rectangle::<i32, Client>::new((x, y).into(), (width, height).into())
                    .to_f64()
                    .to_logical(client_scale)
                    .to_i32_round(),
            )),
            wl_region::Request::Subtract { x, y, width, height } => guard.rects.push((
                RectangleKind::Subtract,
                Rectangle::<i32, Client>::new((x, y).into(), (width, height).into())
                    .to_f64()
                    .to_logical(client_scale)
                    .to_i32_round(),
            )),
            wl_region::Request::Destroy => {
                // all is handled by our destructor
            }
            _ => unreachable!(),
        }
    }
}

/*
 * wl_subcompositor
 */

impl<D> GlobalDispatch<WlSubcompositor, (), D> for CompositorState
where
    D: GlobalDispatch<WlSubcompositor, ()>,
    D: Dispatch<WlSubcompositor, ()>,
    D: Dispatch<WlSubsurface, SubsurfaceUserData>,
    D: CompositorHandler,
    D: 'static,
{
    fn bind(
        _state: &mut D,
        _handle: &DisplayHandle,
        _client: &wayland_server::Client,
        resource: New<WlSubcompositor>,
        _global_data: &(),
        data_init: &mut DataInit<'_, D>,
    ) {
        data_init.init(resource, ());
    }
}

impl<D> Dispatch<WlSubcompositor, (), D> for CompositorState
where
    D: Dispatch<WlSubcompositor, ()>,
    D: Dispatch<WlSubsurface, SubsurfaceUserData>,
    D: CompositorHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &wayland_server::Client,
        subcompositor: &WlSubcompositor,
        request: wl_subcompositor::Request,
        _data: &(),
        _dh: &DisplayHandle,
        data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            wl_subcompositor::Request::GetSubsurface { id, surface, parent } => {
                if let Err(AlreadyHasRole) = PrivateSurfaceData::set_parent(&surface, &parent) {
                    subcompositor
                        .post_error(wl_subcompositor::Error::BadSurface, "Surface already has a role.");
                    return;
                }

                data_init.init(
                    id,
                    SubsurfaceUserData {
                        surface: surface.clone(),
                    },
                );

                super::with_states(&surface, |states| {
                    states.data_map.insert_if_missing_threadsafe(SubsurfaceState::new)
                });

                state.new_subsurface(&surface, &parent);
            }
            wl_subcompositor::Request::Destroy => {}
            _ => unreachable!(),
        }
    }
}

/*
 * wl_subsurface
 */

/// User data of WlSubsurface
#[derive(Debug)]
pub struct SubsurfaceUserData {
    surface: WlSurface,
}

impl SubsurfaceUserData {
    /// Returns the surface for this subsurface (not to be confused with the parent surface).
    pub fn surface(&self) -> &WlSurface {
        &self.surface
    }
}
/// The cached state associated with a subsurface
#[derive(Debug)]
pub struct SubsurfaceCachedState {
    /// Location of the top-left corner of this subsurface
    /// relative to its parent coordinate space
    pub location: Point<i32, Logical>,
}

impl Default for SubsurfaceCachedState {
    fn default() -> Self {
        SubsurfaceCachedState {
            location: (0, 0).into(),
        }
    }
}

impl Cacheable for SubsurfaceCachedState {
    fn commit(&mut self, _dh: &DisplayHandle) -> Self {
        Self {
            location: self.location,
        }
    }

    fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
        into.location = self.location;
    }
}

pub(crate) struct SubsurfaceState {
    pub(crate) sync: AtomicBool,
}

impl SubsurfaceState {
    fn new() -> SubsurfaceState {
        SubsurfaceState {
            sync: AtomicBool::new(true),
        }
    }
}

/// Check if a (sub)surface is effectively sync
pub fn is_effectively_sync(surface: &wl_surface::WlSurface) -> bool {
    let is_direct_sync = PrivateSurfaceData::with_states(surface, |state| {
        state
            .data_map
            .get::<SubsurfaceState>()
            .map(|s| s.sync.load(Ordering::Acquire))
            .unwrap_or(false)
    });
    if is_direct_sync {
        return true;
    }
    if let Some(parent) = PrivateSurfaceData::get_parent(surface) {
        is_effectively_sync(&parent)
    } else {
        false
    }
}

impl<D> Dispatch<WlSubsurface, SubsurfaceUserData, D> for CompositorState
where
    D: Dispatch<WlSubsurface, SubsurfaceUserData>,
    D: CompositorHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        client: &wayland_server::Client,
        subsurface: &WlSubsurface,
        request: wl_subsurface::Request,
        data: &SubsurfaceUserData,
        _dh: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            wl_subsurface::Request::SetPosition { x, y } => {
                let client_scale = state.client_compositor_state(client).client_scale();
                PrivateSurfaceData::with_states(&data.surface, |state| {
                    state
                        .cached_state
                        .get::<SubsurfaceCachedState>()
                        .pending()
                        .location = Point::<i32, Client>::from((x, y))
                        .to_f64()
                        .to_logical(client_scale)
                        .to_i32_round();
                })
            }
            wl_subsurface::Request::PlaceAbove { sibling } => {
                if let Err(()) = PrivateSurfaceData::reorder(&data.surface, Location::After, &sibling) {
                    subsurface.post_error(
                        wl_subsurface::Error::BadSurface,
                        "Provided surface is not a sibling or parent.",
                    )
                }
            }
            wl_subsurface::Request::PlaceBelow { sibling } => {
                if let Err(()) = PrivateSurfaceData::reorder(&data.surface, Location::Before, &sibling) {
                    subsurface.post_error(
                        wl_subsurface::Error::BadSurface,
                        "Provided surface is not a sibling or parent.",
                    )
                }
            }
            wl_subsurface::Request::SetSync => PrivateSurfaceData::with_states(&data.surface, |state| {
                state
                    .data_map
                    .get::<SubsurfaceState>()
                    .unwrap()
                    .sync
                    .store(true, Ordering::Release);
            }),
            wl_subsurface::Request::SetDesync => PrivateSurfaceData::with_states(&data.surface, |state| {
                state
                    .data_map
                    .get::<SubsurfaceState>()
                    .unwrap()
                    .sync
                    .store(false, Ordering::Release);
            }),
            wl_subsurface::Request::Destroy => {
                // Our destructor already handles it
            }
            _ => unreachable!(),
        }
    }

    fn destroyed(
        _state: &mut D,
        _client_id: wayland_server::backend::ClientId,
        _object: &WlSubsurface,
        data: &SubsurfaceUserData,
    ) {
        PrivateSurfaceData::unset_parent(&data.surface);
        PrivateSurfaceData::with_states(&data.surface, |state| {
            state
                .data_map
                .get::<SubsurfaceState>()
                .unwrap()
                .sync
                .store(true, Ordering::Release);

            let mut guard = state.cached_state.get::<SubsurfaceCachedState>();
            *guard.pending() = Default::default();
            *guard.current() = Default::default();
        });
    }
}

impl<D> Dispatch<WlCallback, (), D> for CompositorState
where
    D: Dispatch<WlCallback, ()>,
    D: CompositorHandler,
    D: 'static,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        _subsurface: &WlCallback,
        _request: wl_callback::Request,
        _data: &(),
        _handle: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
    }
}