-
Notifications
You must be signed in to change notification settings - Fork 175
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1011 from PolyMeilex/wp-content-type
- Loading branch information
Showing
3 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
use wayland_protocols::wp::content_type::v1::server::{ | ||
wp_content_type_manager_v1::{self, WpContentTypeManagerV1}, | ||
wp_content_type_v1::{self, WpContentTypeV1}, | ||
}; | ||
use wayland_server::{ | ||
backend::{ClientId, ObjectId}, | ||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, | ||
}; | ||
|
||
use super::{ContentTypeState, ContentTypeSurfaceCachedState, ContentTypeSurfaceData, ContentTypeUserData}; | ||
use crate::wayland::compositor; | ||
|
||
impl<D> GlobalDispatch<WpContentTypeManagerV1, (), D> for ContentTypeState | ||
where | ||
D: GlobalDispatch<WpContentTypeManagerV1, ()>, | ||
D: Dispatch<WpContentTypeManagerV1, ()>, | ||
D: Dispatch<WpContentTypeV1, ContentTypeUserData>, | ||
D: 'static, | ||
{ | ||
fn bind( | ||
_state: &mut D, | ||
_: &DisplayHandle, | ||
_: &Client, | ||
resource: New<WpContentTypeManagerV1>, | ||
_: &(), | ||
data_init: &mut DataInit<'_, D>, | ||
) { | ||
data_init.init(resource, ()); | ||
} | ||
} | ||
|
||
impl<D> Dispatch<WpContentTypeManagerV1, (), D> for ContentTypeState | ||
where | ||
D: Dispatch<WpContentTypeManagerV1, ()>, | ||
D: Dispatch<WpContentTypeV1, ContentTypeUserData>, | ||
D: 'static, | ||
{ | ||
fn request( | ||
_state: &mut D, | ||
_: &Client, | ||
manager: &wp_content_type_manager_v1::WpContentTypeManagerV1, | ||
request: wp_content_type_manager_v1::Request, | ||
_data: &(), | ||
_dh: &DisplayHandle, | ||
data_init: &mut DataInit<'_, D>, | ||
) { | ||
match request { | ||
wp_content_type_manager_v1::Request::GetSurfaceContentType { id, surface } => { | ||
let already_taken = compositor::with_states(&surface, |states| { | ||
states | ||
.data_map | ||
.insert_if_missing_threadsafe(ContentTypeSurfaceData::new); | ||
let data = states.data_map.get::<ContentTypeSurfaceData>().unwrap(); | ||
|
||
let already_taken = data.is_resource_attached(); | ||
|
||
if !already_taken { | ||
data.set_is_resource_attached(true); | ||
} | ||
|
||
already_taken | ||
}); | ||
|
||
if already_taken { | ||
manager.post_error( | ||
wp_content_type_manager_v1::Error::AlreadyConstructed, | ||
"WlSurface already has WpSurfaceContentType attached", | ||
) | ||
} else { | ||
data_init.init(id, ContentTypeUserData::new(surface)); | ||
} | ||
} | ||
|
||
wp_content_type_manager_v1::Request::Destroy => {} | ||
_ => unreachable!(), | ||
} | ||
} | ||
} | ||
|
||
impl<D> Dispatch<WpContentTypeV1, ContentTypeUserData, D> for ContentTypeState | ||
where | ||
D: Dispatch<WpContentTypeV1, ContentTypeUserData>, | ||
{ | ||
fn request( | ||
_state: &mut D, | ||
_: &Client, | ||
_: &WpContentTypeV1, | ||
request: wp_content_type_v1::Request, | ||
data: &ContentTypeUserData, | ||
_dh: &DisplayHandle, | ||
_: &mut DataInit<'_, D>, | ||
) { | ||
match request { | ||
wp_content_type_v1::Request::SetContentType { content_type } => { | ||
let wayland_server::WEnum::Value(content_type) = content_type else { return; }; | ||
let Some(surface) = data.wl_surface() else { return; }; | ||
|
||
compositor::with_states(&surface, |states| { | ||
states | ||
.cached_state | ||
.pending::<ContentTypeSurfaceCachedState>() | ||
.content_type = content_type; | ||
}) | ||
} | ||
// Switch back to not specifying the content type of this surface. | ||
// This is equivalent to setting the content type to none, | ||
// including double buffering semantics. | ||
wp_content_type_v1::Request::Destroy => { | ||
let Some(surface) = data.wl_surface() else { return; }; | ||
|
||
compositor::with_states(&surface, |states| { | ||
states | ||
.data_map | ||
.get::<ContentTypeSurfaceData>() | ||
.unwrap() | ||
.set_is_resource_attached(false); | ||
|
||
states | ||
.cached_state | ||
.pending::<ContentTypeSurfaceCachedState>() | ||
.content_type = wp_content_type_v1::Type::None; | ||
}); | ||
} | ||
_ => unreachable!(), | ||
} | ||
} | ||
|
||
fn destroyed(_state: &mut D, _client: ClientId, _object: ObjectId, _data: &ContentTypeUserData) { | ||
// Nothing to do here, graceful Destroy is already handled with double buffering | ||
// and in case of client close WlSurface destroyed handler will clean up the data anyway, | ||
// so there is no point in queuing new update | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
//! Implementation of wp_content_type protocol | ||
//! | ||
//! ### Example | ||
//! | ||
//! ```no_run | ||
//! # extern crate wayland_server; | ||
//! # | ||
//! use wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}; | ||
//! use smithay::{ | ||
//! delegate_content_type, delegate_compositor, | ||
//! wayland::compositor::{self, CompositorState, CompositorHandler}, | ||
//! wayland::content_type::{ContentTypeSurfaceCachedState, ContentTypeState}, | ||
//! }; | ||
//! | ||
//! pub struct State { | ||
//! compositor_state: CompositorState, | ||
//! }; | ||
//! | ||
//! delegate_content_type!(State); | ||
//! delegate_compositor!(State); | ||
//! | ||
//! impl CompositorHandler for State { | ||
//! fn compositor_state(&mut self) -> &mut CompositorState { | ||
//! &mut self.compositor_state | ||
//! } | ||
//! | ||
//! fn commit(&mut self, surface: &WlSurface) { | ||
//! compositor::with_states(&surface, |states| { | ||
//! let current = states.cached_state.current::<ContentTypeSurfaceCachedState>(); | ||
//! dbg!(current.content_type()); | ||
//! }); | ||
//! } | ||
//! } | ||
//! | ||
//! let mut display = wayland_server::Display::<State>::new().unwrap(); | ||
//! | ||
//! let compositor_state = CompositorState::new::<State>(&display.handle()); | ||
//! ContentTypeState::new::<State>(&display.handle()); | ||
//! | ||
//! let state = State { | ||
//! compositor_state, | ||
//! }; | ||
//! ``` | ||
use std::sync::{ | ||
atomic::{self, AtomicBool}, | ||
Mutex, | ||
}; | ||
|
||
use wayland_protocols::wp::content_type::v1::server::{ | ||
wp_content_type_manager_v1::WpContentTypeManagerV1, | ||
wp_content_type_v1::{self, WpContentTypeV1}, | ||
}; | ||
use wayland_server::{ | ||
backend::GlobalId, protocol::wl_surface::WlSurface, Dispatch, DisplayHandle, GlobalDispatch, | ||
}; | ||
|
||
use super::compositor::Cacheable; | ||
|
||
mod dispatch; | ||
|
||
/// Data associated with WlSurface | ||
/// Represents the client pending state | ||
/// | ||
/// ```no_run | ||
/// use smithay::wayland::compositor; | ||
/// use smithay::wayland::content_type::ContentTypeSurfaceCachedState; | ||
/// | ||
/// # let wl_surface = todo!(); | ||
/// compositor::with_states(&wl_surface, |states| { | ||
/// let current = states.cached_state.current::<ContentTypeSurfaceCachedState>(); | ||
/// dbg!(current.content_type()); | ||
/// }); | ||
/// ``` | ||
#[derive(Debug, Clone, Copy)] | ||
pub struct ContentTypeSurfaceCachedState { | ||
content_type: wp_content_type_v1::Type, | ||
} | ||
|
||
impl ContentTypeSurfaceCachedState { | ||
/// This informs the compositor that the client believes it is displaying buffers matching this content type. | ||
pub fn content_type(&self) -> &wp_content_type_v1::Type { | ||
&self.content_type | ||
} | ||
} | ||
|
||
impl Default for ContentTypeSurfaceCachedState { | ||
fn default() -> Self { | ||
Self { | ||
content_type: wp_content_type_v1::Type::None, | ||
} | ||
} | ||
} | ||
|
||
impl Cacheable for ContentTypeSurfaceCachedState { | ||
fn commit(&mut self, _dh: &DisplayHandle) -> Self { | ||
*self | ||
} | ||
|
||
fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { | ||
*into = self; | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
struct ContentTypeSurfaceData { | ||
is_resource_attached: AtomicBool, | ||
} | ||
|
||
impl ContentTypeSurfaceData { | ||
fn new() -> Self { | ||
Self { | ||
is_resource_attached: AtomicBool::new(false), | ||
} | ||
} | ||
|
||
fn set_is_resource_attached(&self, is_attached: bool) { | ||
self.is_resource_attached | ||
.store(is_attached, atomic::Ordering::Release) | ||
} | ||
|
||
fn is_resource_attached(&self) -> bool { | ||
self.is_resource_attached.load(atomic::Ordering::Acquire) | ||
} | ||
} | ||
|
||
/// User data of `WpContentTypeV1` object | ||
#[derive(Debug)] | ||
pub struct ContentTypeUserData(Mutex<Option<WlSurface>>); | ||
|
||
impl ContentTypeUserData { | ||
fn new(surface: WlSurface) -> Self { | ||
Self(Mutex::new(Some(surface))) | ||
} | ||
|
||
fn wl_surface(&self) -> Option<WlSurface> { | ||
self.0.lock().unwrap().clone() | ||
} | ||
} | ||
|
||
/// Delegate type for [WpContentTypeManagerV1] global. | ||
#[derive(Debug)] | ||
pub struct ContentTypeState { | ||
global: GlobalId, | ||
} | ||
|
||
impl ContentTypeState { | ||
/// Regiseter new [WpContentTypeManagerV1] global | ||
pub fn new<D>(display: &DisplayHandle) -> ContentTypeState | ||
where | ||
D: GlobalDispatch<WpContentTypeManagerV1, ()> | ||
+ Dispatch<WpContentTypeManagerV1, ()> | ||
+ Dispatch<WpContentTypeV1, ContentTypeUserData> | ||
+ 'static, | ||
{ | ||
let global = display.create_global::<D, WpContentTypeManagerV1, _>(1, ()); | ||
|
||
ContentTypeState { global } | ||
} | ||
|
||
/// Returns the WpContentTypeManagerV1 global id | ||
pub fn global(&self) -> GlobalId { | ||
self.global.clone() | ||
} | ||
} | ||
|
||
/// Macro to delegate implementation of the wp content type protocol | ||
#[macro_export] | ||
macro_rules! delegate_content_type { | ||
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { | ||
type __WpContentTypeManagerV1 = | ||
$crate::reexports::wayland_protocols::wp::content_type::v1::server::wp_content_type_manager_v1::WpContentTypeManagerV1; | ||
type __WpContentTypeV1 = | ||
$crate::reexports::wayland_protocols::wp::content_type::v1::server::wp_content_type_v1::WpContentTypeV1; | ||
|
||
$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: | ||
[ | ||
__WpContentTypeManagerV1: () | ||
] => $crate::wayland::content_type::ContentTypeState | ||
); | ||
|
||
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: | ||
[ | ||
__WpContentTypeManagerV1: () | ||
] => $crate::wayland::content_type::ContentTypeState | ||
); | ||
|
||
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: | ||
[ | ||
__WpContentTypeV1: $crate::wayland::content_type::ContentTypeUserData | ||
] => $crate::wayland::content_type::ContentTypeState | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters