Skip to content

Commit 4be1312

Browse files
committed
Implement wayland side of tearing control
1 parent c293ec7 commit 4be1312

File tree

3 files changed

+342
-0
lines changed

3 files changed

+342
-0
lines changed

src/wayland/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub mod shell;
7070
pub mod shm;
7171
pub mod socket;
7272
pub mod tablet_manager;
73+
pub mod tearing_control;
7374
pub mod text_input;
7475
pub mod viewporter;
7576
pub mod virtual_keyboard;
+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use wayland_protocols::wp::tearing_control::v1::server::{
2+
wp_tearing_control_manager_v1::{self, WpTearingControlManagerV1},
3+
wp_tearing_control_v1::{self, WpTearingControlV1},
4+
};
5+
use wayland_server::{
6+
backend::ClientId, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
7+
};
8+
9+
use super::{
10+
TearingControlState, TearingControlSurfaceCachedState, TearingControlSurfaceData, TearingControlUserData,
11+
};
12+
use crate::wayland::compositor;
13+
14+
impl<D> GlobalDispatch<WpTearingControlManagerV1, (), D> for TearingControlState
15+
where
16+
D: GlobalDispatch<WpTearingControlManagerV1, ()>,
17+
D: Dispatch<WpTearingControlManagerV1, ()>,
18+
D: Dispatch<WpTearingControlV1, TearingControlUserData>,
19+
D: 'static,
20+
{
21+
fn bind(
22+
_state: &mut D,
23+
_: &DisplayHandle,
24+
_: &Client,
25+
resource: New<WpTearingControlManagerV1>,
26+
_: &(),
27+
data_init: &mut DataInit<'_, D>,
28+
) {
29+
data_init.init(resource, ());
30+
}
31+
}
32+
33+
impl<D> Dispatch<WpTearingControlManagerV1, (), D> for TearingControlState
34+
where
35+
D: Dispatch<WpTearingControlManagerV1, ()>,
36+
D: Dispatch<WpTearingControlV1, TearingControlUserData>,
37+
D: 'static,
38+
{
39+
fn request(
40+
_state: &mut D,
41+
_: &Client,
42+
manager: &WpTearingControlManagerV1,
43+
request: wp_tearing_control_manager_v1::Request,
44+
_data: &(),
45+
_dh: &DisplayHandle,
46+
data_init: &mut DataInit<'_, D>,
47+
) {
48+
match request {
49+
wp_tearing_control_manager_v1::Request::GetTearingControl { id, surface } => {
50+
let already_taken = compositor::with_states(&surface, |states| {
51+
states
52+
.data_map
53+
.insert_if_missing_threadsafe(TearingControlSurfaceData::new);
54+
let data = states.data_map.get::<TearingControlSurfaceData>().unwrap();
55+
56+
let already_taken = data.is_resource_attached();
57+
58+
if !already_taken {
59+
data.set_is_resource_attached(true);
60+
}
61+
62+
already_taken
63+
});
64+
65+
if already_taken {
66+
manager.post_error(
67+
wp_tearing_control_manager_v1::Error::TearingControlExists,
68+
"WlSurface already has WpTearingControlV1 attached",
69+
)
70+
} else {
71+
data_init.init(id, TearingControlUserData::new(surface));
72+
}
73+
}
74+
75+
wp_tearing_control_manager_v1::Request::Destroy => {}
76+
_ => unreachable!(),
77+
}
78+
}
79+
}
80+
81+
impl<D> Dispatch<WpTearingControlV1, TearingControlUserData, D> for TearingControlState
82+
where
83+
D: Dispatch<WpTearingControlV1, TearingControlUserData>,
84+
{
85+
fn request(
86+
_state: &mut D,
87+
_: &Client,
88+
_: &WpTearingControlV1,
89+
request: wp_tearing_control_v1::Request,
90+
data: &TearingControlUserData,
91+
_dh: &DisplayHandle,
92+
_: &mut DataInit<'_, D>,
93+
) {
94+
match request {
95+
wp_tearing_control_v1::Request::SetPresentationHint { hint } => {
96+
let wayland_server::WEnum::Value(hint) = hint else {
97+
return;
98+
};
99+
let surface = data.wl_surface();
100+
101+
compositor::with_states(&surface, |states| {
102+
states
103+
.cached_state
104+
.pending::<TearingControlSurfaceCachedState>()
105+
.presentation_hint = hint;
106+
})
107+
}
108+
// Switch back to default PresentationHint.
109+
// This is equivalent to setting the hint to Vsync,
110+
// including double buffering semantics.
111+
wp_tearing_control_v1::Request::Destroy => {
112+
let surface = data.wl_surface();
113+
114+
compositor::with_states(&surface, |states| {
115+
states
116+
.data_map
117+
.get::<TearingControlSurfaceData>()
118+
.unwrap()
119+
.set_is_resource_attached(false);
120+
121+
states
122+
.cached_state
123+
.pending::<TearingControlSurfaceCachedState>()
124+
.presentation_hint = wp_tearing_control_v1::PresentationHint::Vsync;
125+
});
126+
}
127+
_ => unreachable!(),
128+
}
129+
}
130+
131+
fn destroyed(
132+
_state: &mut D,
133+
_client: ClientId,
134+
_object: &WpTearingControlV1,
135+
_data: &TearingControlUserData,
136+
) {
137+
// Nothing to do here, graceful Destroy is already handled with double buffering
138+
// and in case of client close WlSurface destroyed handler will clean up the data anyway,
139+
// so there is no point in queuing new update
140+
}
141+
}

src/wayland/tearing_control/mod.rs

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//! Implementation of wp_tearing_control protocol
2+
//!
3+
//! ### Example
4+
//!
5+
//! ```no_run
6+
//! # extern crate wayland_server;
7+
//! #
8+
//! use wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle};
9+
//! use smithay::{
10+
//! delegate_tearing_control, delegate_compositor,
11+
//! wayland::compositor::{self, CompositorState, CompositorClientState, CompositorHandler},
12+
//! wayland::tearing_control::{TearingControlSurfaceCachedState, TearingControlState},
13+
//! };
14+
//!
15+
//! pub struct State {
16+
//! compositor_state: CompositorState,
17+
//! };
18+
//! struct ClientState { compositor_state: CompositorClientState }
19+
//! impl wayland_server::backend::ClientData for ClientState {}
20+
//!
21+
//! delegate_tearing_control!(State);
22+
//! delegate_compositor!(State);
23+
//!
24+
//! impl CompositorHandler for State {
25+
//! fn compositor_state(&mut self) -> &mut CompositorState {
26+
//! &mut self.compositor_state
27+
//! }
28+
//!
29+
//! fn client_compositor_state<'a>(&self, client: &'a wayland_server::Client) -> &'a CompositorClientState {
30+
//! &client.get_data::<ClientState>().unwrap().compositor_state
31+
//! }
32+
//!
33+
//! fn commit(&mut self, surface: &WlSurface) {
34+
//! compositor::with_states(&surface, |states| {
35+
//! let current = states.cached_state.current::<TearingControlSurfaceCachedState>();
36+
//! dbg!(current.presentation_hint());
37+
//! });
38+
//! }
39+
//! }
40+
//!
41+
//! let mut display = wayland_server::Display::<State>::new().unwrap();
42+
//!
43+
//! let compositor_state = CompositorState::new::<State>(&display.handle());
44+
//! TearingControlState::new::<State>(&display.handle());
45+
//!
46+
//! let state = State {
47+
//! compositor_state,
48+
//! };
49+
//! ```
50+
51+
use std::sync::{
52+
atomic::{self, AtomicBool},
53+
Mutex,
54+
};
55+
56+
use wayland_protocols::wp::tearing_control::v1::server::{
57+
wp_tearing_control_manager_v1::WpTearingControlManagerV1,
58+
wp_tearing_control_v1::{self, WpTearingControlV1},
59+
};
60+
use wayland_server::{
61+
backend::GlobalId, protocol::wl_surface::WlSurface, Dispatch, DisplayHandle, GlobalDispatch,
62+
};
63+
64+
use super::compositor::Cacheable;
65+
66+
mod dispatch;
67+
68+
/// Data associated with WlSurface
69+
/// Represents the client pending state
70+
///
71+
/// ```no_run
72+
/// use smithay::wayland::compositor;
73+
/// use smithay::wayland::tearing_control::TearingControlSurfaceCachedState;
74+
///
75+
/// # let wl_surface = todo!();
76+
/// compositor::with_states(&wl_surface, |states| {
77+
/// let current = states.cached_state.current::<TearingControlSurfaceCachedState>();
78+
/// dbg!(current.presentation_hint());
79+
/// });
80+
/// ```
81+
#[derive(Debug, Clone, Copy)]
82+
pub struct TearingControlSurfaceCachedState {
83+
presentation_hint: wp_tearing_control_v1::PresentationHint,
84+
}
85+
86+
impl TearingControlSurfaceCachedState {
87+
/// Provides information for if submitted frames from the client may be presented with tearing.
88+
pub fn presentation_hint(&self) -> &wp_tearing_control_v1::PresentationHint {
89+
&self.presentation_hint
90+
}
91+
}
92+
93+
impl Default for TearingControlSurfaceCachedState {
94+
fn default() -> Self {
95+
Self {
96+
presentation_hint: wp_tearing_control_v1::PresentationHint::Vsync,
97+
}
98+
}
99+
}
100+
101+
impl Cacheable for TearingControlSurfaceCachedState {
102+
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
103+
*self
104+
}
105+
106+
fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
107+
*into = self;
108+
}
109+
}
110+
111+
#[derive(Debug)]
112+
struct TearingControlSurfaceData {
113+
is_resource_attached: AtomicBool,
114+
}
115+
116+
impl TearingControlSurfaceData {
117+
fn new() -> Self {
118+
Self {
119+
is_resource_attached: AtomicBool::new(false),
120+
}
121+
}
122+
123+
fn set_is_resource_attached(&self, is_attached: bool) {
124+
self.is_resource_attached
125+
.store(is_attached, atomic::Ordering::Release)
126+
}
127+
128+
fn is_resource_attached(&self) -> bool {
129+
self.is_resource_attached.load(atomic::Ordering::Acquire)
130+
}
131+
}
132+
133+
/// User data of [WpTearingControlV1] object
134+
#[derive(Debug)]
135+
pub struct TearingControlUserData(Mutex<WlSurface>);
136+
137+
impl TearingControlUserData {
138+
fn new(surface: WlSurface) -> Self {
139+
Self(Mutex::new(surface))
140+
}
141+
142+
fn wl_surface(&self) -> WlSurface {
143+
self.0.lock().unwrap().clone()
144+
}
145+
}
146+
147+
/// Delegate type for [WpTearingControlManagerV1] global.
148+
#[derive(Debug)]
149+
pub struct TearingControlState {
150+
global: GlobalId,
151+
}
152+
153+
impl TearingControlState {
154+
/// Regiseter new [WpTearingControlManagerV1] global
155+
pub fn new<D>(display: &DisplayHandle) -> TearingControlState
156+
where
157+
D: GlobalDispatch<WpTearingControlManagerV1, ()>
158+
+ Dispatch<WpTearingControlManagerV1, ()>
159+
+ Dispatch<WpTearingControlV1, TearingControlUserData>
160+
+ 'static,
161+
{
162+
let global = display.create_global::<D, WpTearingControlManagerV1, _>(1, ());
163+
164+
TearingControlState { global }
165+
}
166+
167+
/// Returns the [WpTearingControlManagerV1] global id
168+
pub fn global(&self) -> GlobalId {
169+
self.global.clone()
170+
}
171+
}
172+
173+
/// Macro to delegate implementation of the wp tearing control protocol
174+
#[macro_export]
175+
macro_rules! delegate_tearing_control {
176+
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
177+
type __WpTearingControlManagerV1 =
178+
$crate::reexports::wayland_protocols::wp::tearing_control::v1::server::wp_tearing_control_manager_v1::WpTearingControlManagerV1;
179+
type __WpTearingControlV1 =
180+
$crate::reexports::wayland_protocols::wp::tearing_control::v1::server::wp_tearing_control_v1::WpTearingControlV1;
181+
182+
$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
183+
[
184+
__WpTearingControlManagerV1: ()
185+
] => $crate::wayland::tearing_control::TearingControlState
186+
);
187+
188+
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
189+
[
190+
__WpTearingControlManagerV1: ()
191+
] => $crate::wayland::tearing_control::TearingControlState
192+
);
193+
194+
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
195+
[
196+
__WpTearingControlV1: $crate::wayland::tearing_control::TearingControlUserData
197+
] => $crate::wayland::tearing_control::TearingControlState
198+
);
199+
};
200+
}

0 commit comments

Comments
 (0)