use std::{cell::RefCell, rc::Rc, sync::Mutex}; use wayland_server::{ protocol::{wl_compositor, wl_region, wl_subcompositor, wl_subsurface, wl_surface}, DisplayToken, NewResource, Resource, }; use super::{ tree::{Location, SurfaceData}, CompositorToken, Damage, Rectangle, RectangleKind, RegionAttributes, Role, RoleType, SubsurfaceRole, SurfaceEvent, }; /* * wl_compositor */ pub(crate) fn implement_compositor( compositor: NewResource, token: DisplayToken, log: ::slog::Logger, implem: Rc>, ) -> Resource where U: Default + 'static, R: Default + 'static, Impl: FnMut(SurfaceEvent, Resource, CompositorToken) + 'static, { let my_token = token.clone(); compositor.implement_nonsend( move |request, _compositor| match request { wl_compositor::Request::CreateSurface { id } => { trace!(log, "Creating a new wl_surface."); implement_surface(id, &token, log.clone(), implem.clone()); } wl_compositor::Request::CreateRegion { id } => { trace!(log, "Creating a new wl_region."); implement_region(id, &token); } }, None::, (), &my_token, ) } /* * wl_surface */ // Internal implementation data of surfaces pub(crate) struct SurfaceImplem { log: ::slog::Logger, implem: Rc, CompositorToken)>>, } impl SurfaceImplem { fn make(log: ::slog::Logger, implem: Rc>) -> SurfaceImplem where Impl: FnMut(SurfaceEvent, Resource, CompositorToken) + 'static, { SurfaceImplem { log, implem } } } impl SurfaceImplem where U: 'static, R: 'static, { fn receive_surface_request( &mut self, req: wl_surface::Request, surface: Resource, ) { match req { wl_surface::Request::Attach { buffer, x, y } => { SurfaceData::::with_data(&surface, |d| { d.buffer = Some(buffer.map(|b| (b.clone(), (x, y)))) }); } wl_surface::Request::Damage { x, y, width, height } => { SurfaceData::::with_data(&surface, |d| { d.damage = Damage::Surface(Rectangle { x, y, width, height }) }); } wl_surface::Request::Frame { callback } => { let mut user_impl = self.implem.borrow_mut(); trace!(self.log, "Calling user implementation for wl_surface.frame"); (&mut *user_impl)(SurfaceEvent::Frame { callback }, surface, CompositorToken::make()); } wl_surface::Request::SetOpaqueRegion { region } => { let attributes = region.map(|r| { let attributes_mutex = r.user_data::>().unwrap(); attributes_mutex.lock().unwrap().clone() }); SurfaceData::::with_data(&surface, |d| d.opaque_region = attributes); } wl_surface::Request::SetInputRegion { region } => { let attributes = region.map(|r| { let attributes_mutex = r.user_data::>().unwrap(); attributes_mutex.lock().unwrap().clone() }); SurfaceData::::with_data(&surface, |d| d.input_region = attributes); } wl_surface::Request::Commit => { let mut user_impl = self.implem.borrow_mut(); trace!(self.log, "Calling user implementation for wl_surface.commit"); (&mut *user_impl)(SurfaceEvent::Commit, surface, CompositorToken::make()); } wl_surface::Request::SetBufferTransform { transform } => { SurfaceData::::with_data(&surface, |d| d.buffer_transform = transform); } wl_surface::Request::SetBufferScale { scale } => { SurfaceData::::with_data(&surface, |d| d.buffer_scale = scale); } wl_surface::Request::DamageBuffer { x, y, width, height } => { SurfaceData::::with_data(&surface, |d| { d.damage = Damage::Buffer(Rectangle { x, y, width, height }) }); } wl_surface::Request::Destroy => { // All is already handled by our destructor } } } } fn implement_surface( surface: NewResource, token: &DisplayToken, log: ::slog::Logger, implem: Rc>, ) -> Resource where U: Default + 'static, R: Default + 'static, Impl: FnMut(SurfaceEvent, Resource, CompositorToken) + 'static, { let surface = surface.implement_nonsend( { let mut implem = SurfaceImplem::make(log, implem); move |req, surface| implem.receive_surface_request(req, surface) }, Some(|surface| SurfaceData::::cleanup(&surface)), SurfaceData::::new(), token, ); surface } /* * wl_region */ fn region_implem(request: wl_region::Request, region: Resource) { let attributes_mutex = region.user_data::>().unwrap(); let mut guard = attributes_mutex.lock().unwrap(); match request { wl_region::Request::Add { x, y, width, height } => guard .rects .push((RectangleKind::Add, Rectangle { x, y, width, height })), wl_region::Request::Subtract { x, y, width, height } => guard .rects .push((RectangleKind::Subtract, Rectangle { x, y, width, height })), wl_region::Request::Destroy => { // all is handled by our destructor } } } fn implement_region( region: NewResource, token: &DisplayToken, ) -> Resource { region.implement_nonsend( region_implem, None::, Mutex::new(RegionAttributes::default()), token, ) } /* * wl_subcompositor */ pub(crate) fn implement_subcompositor( subcompositor: NewResource, token: DisplayToken, ) -> Resource where R: RoleType + Role + 'static, U: 'static, { let my_token = token.clone(); subcompositor.implement_nonsend( move |request, subcompositor: Resource<_>| match request { wl_subcompositor::Request::GetSubsurface { id, surface, parent } => { if let Err(()) = SurfaceData::::set_parent(&surface, &parent) { subcompositor.post_error( wl_subcompositor::Error::BadSurface as u32, "Surface already has a role.".into(), ); return; } implement_subsurface::(id, surface.clone(), &token); } wl_subcompositor::Request::Destroy => {} }, None::, (), &my_token, ) } /* * wl_subsurface */ fn with_subsurface_attributes(subsurface: &Resource, f: F) where F: FnOnce(&mut SubsurfaceRole), U: 'static, R: RoleType + Role + 'static, { let surface = subsurface.user_data::>().unwrap(); SurfaceData::::with_role_data::(surface, |d| f(d)) .expect("The surface does not have a subsurface role while it has a wl_subsurface?!"); } fn implement_subsurface( subsurface: NewResource, surface: Resource, token: &DisplayToken, ) -> Resource where U: 'static, R: RoleType + Role + 'static, { subsurface.implement_nonsend( |request, subsurface| { match request { wl_subsurface::Request::SetPosition { x, y } => { with_subsurface_attributes::(&subsurface, |attrs| { attrs.location = (x, y); }) } wl_subsurface::Request::PlaceAbove { sibling } => { let surface = subsurface.user_data::>().unwrap(); if let Err(()) = SurfaceData::::reorder(surface, Location::After, &sibling) { subsurface.post_error( wl_subsurface::Error::BadSurface as u32, "Provided surface is not a sibling or parent.".into(), ) } } wl_subsurface::Request::PlaceBelow { sibling } => { let surface = subsurface.user_data::>().unwrap(); if let Err(()) = SurfaceData::::reorder(surface, Location::Before, &sibling) { subsurface.post_error( wl_subsurface::Error::BadSurface as u32, "Provided surface is not a sibling or parent.".into(), ) } } wl_subsurface::Request::SetSync => { with_subsurface_attributes::(&subsurface, |attrs| { attrs.sync = true; }) } wl_subsurface::Request::SetDesync => { with_subsurface_attributes::(&subsurface, |attrs| { attrs.sync = false; }) } wl_subsurface::Request::Destroy => { // Our destructor already handles it } } }, Some(|subsurface| destroy_subsurface::(&subsurface)), surface, token, ) } fn destroy_subsurface(subsurface: &Resource) where U: 'static, R: RoleType + Role + 'static, { let surface = subsurface.user_data::>().unwrap(); if surface.is_alive() { SurfaceData::::unset_parent(&surface); } }