Skip to content

Commit

Permalink
Merge pull request #1011 from PolyMeilex/wp-content-type
Browse files Browse the repository at this point in the history
  • Loading branch information
Drakulix authored May 4, 2023
2 parents f7851c1 + 0687f1e commit 4ea0d0b
Show file tree
Hide file tree
Showing 3 changed files with 328 additions and 0 deletions.
133 changes: 133 additions & 0 deletions src/wayland/content_type/dispatch.rs
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
}
}
194 changes: 194 additions & 0 deletions src/wayland/content_type/mod.rs
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
);
};
}
1 change: 1 addition & 0 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
pub mod buffer;
pub mod compositor;
pub mod content_type;
pub mod data_device;
pub mod dmabuf;
pub mod fractional_scale;
Expand Down

0 comments on commit 4ea0d0b

Please sign in to comment.