Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement wayland side of tearing control #1325

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub mod shell;
pub mod shm;
pub mod socket;
pub mod tablet_manager;
pub mod tearing_control;
pub mod text_input;
pub mod viewporter;
pub mod virtual_keyboard;
Expand Down
141 changes: 141 additions & 0 deletions src/wayland/tearing_control/dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use wayland_protocols::wp::tearing_control::v1::server::{
wp_tearing_control_manager_v1::{self, WpTearingControlManagerV1},
wp_tearing_control_v1::{self, WpTearingControlV1},
};
use wayland_server::{
backend::ClientId, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
};

use super::{
TearingControlState, TearingControlSurfaceCachedState, TearingControlSurfaceData, TearingControlUserData,
};
use crate::wayland::compositor;

impl<D> GlobalDispatch<WpTearingControlManagerV1, (), D> for TearingControlState
where
D: GlobalDispatch<WpTearingControlManagerV1, ()>,
D: Dispatch<WpTearingControlManagerV1, ()>,
D: Dispatch<WpTearingControlV1, TearingControlUserData>,
D: 'static,
{
fn bind(
_state: &mut D,
_: &DisplayHandle,
_: &Client,
resource: New<WpTearingControlManagerV1>,
_: &(),
data_init: &mut DataInit<'_, D>,
) {
data_init.init(resource, ());
}
}

impl<D> Dispatch<WpTearingControlManagerV1, (), D> for TearingControlState
where
D: Dispatch<WpTearingControlManagerV1, ()>,
D: Dispatch<WpTearingControlV1, TearingControlUserData>,
D: 'static,
{
fn request(
_state: &mut D,
_: &Client,
manager: &WpTearingControlManagerV1,
request: wp_tearing_control_manager_v1::Request,
_data: &(),
_dh: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
wp_tearing_control_manager_v1::Request::GetTearingControl { id, surface } => {
let already_taken = compositor::with_states(&surface, |states| {
states
.data_map
.insert_if_missing_threadsafe(TearingControlSurfaceData::new);
let data = states.data_map.get::<TearingControlSurfaceData>().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_tearing_control_manager_v1::Error::TearingControlExists,
"WlSurface already has WpTearingControlV1 attached",
)
} else {
data_init.init(id, TearingControlUserData::new(surface));
}
}

wp_tearing_control_manager_v1::Request::Destroy => {}
_ => unreachable!(),
}
}
}

impl<D> Dispatch<WpTearingControlV1, TearingControlUserData, D> for TearingControlState
where
D: Dispatch<WpTearingControlV1, TearingControlUserData>,
{
fn request(
_state: &mut D,
_: &Client,
_: &WpTearingControlV1,
request: wp_tearing_control_v1::Request,
data: &TearingControlUserData,
_dh: &DisplayHandle,
_: &mut DataInit<'_, D>,
) {
match request {
wp_tearing_control_v1::Request::SetPresentationHint { hint } => {
let wayland_server::WEnum::Value(hint) = hint else {
return;
};
let surface = data.wl_surface();

compositor::with_states(&surface, |states| {
states
.cached_state
.pending::<TearingControlSurfaceCachedState>()
.presentation_hint = hint;
})
}
// Switch back to default PresentationHint.
// This is equivalent to setting the hint to Vsync,
// including double buffering semantics.
wp_tearing_control_v1::Request::Destroy => {
let surface = data.wl_surface();

compositor::with_states(&surface, |states| {
states
.data_map
.get::<TearingControlSurfaceData>()
.unwrap()
.set_is_resource_attached(false);

states
.cached_state
.pending::<TearingControlSurfaceCachedState>()
.presentation_hint = wp_tearing_control_v1::PresentationHint::Vsync;
});
}
_ => unreachable!(),
}
}

fn destroyed(
_state: &mut D,
_client: ClientId,
_object: &WpTearingControlV1,
_data: &TearingControlUserData,
) {
// 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
}
}
200 changes: 200 additions & 0 deletions src/wayland/tearing_control/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
//! Implementation of wp_tearing_control protocol
//!
//! ### Example
//!
//! ```no_run
//! # extern crate wayland_server;
//! #
//! use wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle};
//! use smithay::{
//! delegate_tearing_control, delegate_compositor,
//! wayland::compositor::{self, CompositorState, CompositorClientState, CompositorHandler},
//! wayland::tearing_control::{TearingControlSurfaceCachedState, TearingControlState},
//! };
//!
//! pub struct State {
//! compositor_state: CompositorState,
//! };
//! struct ClientState { compositor_state: CompositorClientState }
//! impl wayland_server::backend::ClientData for ClientState {}
//!
//! delegate_tearing_control!(State);
//! delegate_compositor!(State);
//!
//! impl CompositorHandler for State {
//! fn compositor_state(&mut self) -> &mut CompositorState {
//! &mut self.compositor_state
//! }
//!
//! fn client_compositor_state<'a>(&self, client: &'a wayland_server::Client) -> &'a CompositorClientState {
//! &client.get_data::<ClientState>().unwrap().compositor_state
//! }
//!
//! fn commit(&mut self, surface: &WlSurface) {
//! compositor::with_states(&surface, |states| {
//! let current = states.cached_state.current::<TearingControlSurfaceCachedState>();
//! dbg!(current.presentation_hint());
//! });
//! }
//! }
//!
//! let mut display = wayland_server::Display::<State>::new().unwrap();
//!
//! let compositor_state = CompositorState::new::<State>(&display.handle());
//! TearingControlState::new::<State>(&display.handle());
//!
//! let state = State {
//! compositor_state,
//! };
//! ```

use std::sync::{
atomic::{self, AtomicBool},
Mutex,
};

use wayland_protocols::wp::tearing_control::v1::server::{
wp_tearing_control_manager_v1::WpTearingControlManagerV1,
wp_tearing_control_v1::{self, WpTearingControlV1},
};
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::tearing_control::TearingControlSurfaceCachedState;
///
/// # let wl_surface = todo!();
/// compositor::with_states(&wl_surface, |states| {
/// let current = states.cached_state.current::<TearingControlSurfaceCachedState>();
/// dbg!(current.presentation_hint());
/// });
/// ```
#[derive(Debug, Clone, Copy)]
pub struct TearingControlSurfaceCachedState {
presentation_hint: wp_tearing_control_v1::PresentationHint,
}

impl TearingControlSurfaceCachedState {
/// Provides information for if submitted frames from the client may be presented with tearing.
pub fn presentation_hint(&self) -> &wp_tearing_control_v1::PresentationHint {
&self.presentation_hint
}
}

impl Default for TearingControlSurfaceCachedState {
fn default() -> Self {
Self {
presentation_hint: wp_tearing_control_v1::PresentationHint::Vsync,
}
}
}

impl Cacheable for TearingControlSurfaceCachedState {
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
*self
}

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

#[derive(Debug)]
struct TearingControlSurfaceData {
is_resource_attached: AtomicBool,
}

impl TearingControlSurfaceData {
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 [WpTearingControlV1] object
#[derive(Debug)]
pub struct TearingControlUserData(Mutex<WlSurface>);

impl TearingControlUserData {
fn new(surface: WlSurface) -> Self {
Self(Mutex::new(surface))
}

fn wl_surface(&self) -> WlSurface {
self.0.lock().unwrap().clone()
}
}

/// Delegate type for [WpTearingControlManagerV1] global.
#[derive(Debug)]
pub struct TearingControlState {
global: GlobalId,
}

impl TearingControlState {
/// Regiseter new [WpTearingControlManagerV1] global
pub fn new<D>(display: &DisplayHandle) -> TearingControlState
where
D: GlobalDispatch<WpTearingControlManagerV1, ()>
+ Dispatch<WpTearingControlManagerV1, ()>
+ Dispatch<WpTearingControlV1, TearingControlUserData>
+ 'static,
{
let global = display.create_global::<D, WpTearingControlManagerV1, _>(1, ());

TearingControlState { global }
}

/// Returns the [WpTearingControlManagerV1] global id
pub fn global(&self) -> GlobalId {
self.global.clone()
}
}

/// Macro to delegate implementation of the wp tearing control protocol
#[macro_export]
macro_rules! delegate_tearing_control {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
type __WpTearingControlManagerV1 =
$crate::reexports::wayland_protocols::wp::tearing_control::v1::server::wp_tearing_control_manager_v1::WpTearingControlManagerV1;
type __WpTearingControlV1 =
$crate::reexports::wayland_protocols::wp::tearing_control::v1::server::wp_tearing_control_v1::WpTearingControlV1;

$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
[
__WpTearingControlManagerV1: ()
] => $crate::wayland::tearing_control::TearingControlState
);

$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
[
__WpTearingControlManagerV1: ()
] => $crate::wayland::tearing_control::TearingControlState
);

$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
[
__WpTearingControlV1: $crate::wayland::tearing_control::TearingControlUserData
] => $crate::wayland::tearing_control::TearingControlState
);
};
}
Loading