Skip to content

Commit d125eef

Browse files
committed
wayland: add a (multi)surface barrier
1 parent 16efbea commit d125eef

File tree

2 files changed

+111
-3
lines changed

2 files changed

+111
-3
lines changed

src/wayland/compositor/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ use std::{any::Any, sync::Mutex};
118118
pub use self::cache::{Cacheable, CachedState, MultiCache};
119119
pub use self::handlers::{RegionUserData, SubsurfaceCachedState, SubsurfaceUserData, SurfaceUserData};
120120
use self::transaction::TransactionQueue;
121-
pub use self::transaction::{Barrier, Blocker, BlockerState, BlockerKind};
121+
pub use self::transaction::{Barrier, Blocker, BlockerKind, BlockerState, SurfaceBarrier};
122122
pub use self::tree::{AlreadyHasRole, TraversalAction};
123123
use self::tree::{PrivateSurfaceData, SuggestedSurfaceState};
124124
pub use crate::utils::hook::HookId;

src/wayland/compositor/transaction.rs

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,17 @@
3939
// but will be once proper transaction & blockers support is
4040
// added to smithay
4141
use std::{
42-
collections::HashSet,
42+
collections::{HashMap, HashSet},
4343
fmt,
4444
sync::{atomic::AtomicBool, Arc, Mutex},
4545
};
4646

47+
use calloop::ping::Ping;
4748
use wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource, Weak};
4849

4950
use crate::utils::Serial;
5051

51-
use super::{tree::PrivateSurfaceData, CompositorHandler};
52+
use super::{add_blocker, tree::PrivateSurfaceData, CompositorHandler};
5253

5354
/// Kind for a [`Blocker`]
5455
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -129,6 +130,113 @@ impl Blocker for Barrier {
129130
}
130131
}
131132

133+
/// A barrier waiting for multiple surfaces to be notified
134+
#[derive(Debug, Clone)]
135+
pub struct SurfaceBarrier {
136+
barrier: Barrier,
137+
ping: Ping,
138+
ready: Arc<AtomicBool>,
139+
surfaces: Arc<Mutex<HashMap<Weak<WlSurface>, bool>>>,
140+
}
141+
142+
impl SurfaceBarrier {
143+
/// Initialize an empty surface barrier
144+
///
145+
/// [`Ping`] will be used to notify once all [`BlockerKind::Immediate`] blockers are cleared for tracked surfaces.
146+
/// On receiving the ping [`SurfaceBarrier::release`] has to be called to clear the surface barrier blockers and
147+
/// let the tracked surfaces make progress.
148+
pub fn new(ping: Ping) -> Self {
149+
Self::with_surfaces(ping, [])
150+
}
151+
152+
/// Initializes a new [`SurfaceBarrier`] from a list of [`WlSurface`]
153+
///
154+
/// See [`SurfaceBarrier::new`] for more information.
155+
pub fn with_surfaces<'a>(ping: Ping, surfaces: impl IntoIterator<Item = &'a WlSurface>) -> Self {
156+
let barrier = Barrier::new(false);
157+
let ready = Arc::new(AtomicBool::new(false));
158+
let barrier = Self {
159+
barrier,
160+
ping,
161+
ready,
162+
surfaces: Arc::new(Mutex::new(HashMap::new())),
163+
};
164+
barrier.register_surfaces(surfaces);
165+
barrier
166+
}
167+
168+
/// Register surfaces to be tracked by this barrier.
169+
///
170+
/// This will automatically insert a blocker that will resolve on [`SurfaceBarrier::release`].
171+
pub fn register_surfaces<'a>(&self, surfaces: impl IntoIterator<Item = &'a WlSurface>) {
172+
let mut lock = self.surfaces.lock().unwrap();
173+
for surface in surfaces {
174+
if let std::collections::hash_map::Entry::Vacant(vacant_entry) = lock.entry(surface.downgrade()) {
175+
vacant_entry.insert(false);
176+
add_blocker(
177+
surface,
178+
SurfaceBarrierBlocker {
179+
barrier: self.barrier.clone(),
180+
ping: self.ping.clone(),
181+
ready: self.ready.clone(),
182+
surfaces: self.surfaces.clone(),
183+
},
184+
);
185+
}
186+
}
187+
self.ready.store(false, std::sync::atomic::Ordering::Release);
188+
}
189+
190+
/// Release this barrier and clear the blocker on all tracked surfaces
191+
pub fn release<D: CompositorHandler + 'static>(&self, dh: &DisplayHandle, state: &mut D) {
192+
self.barrier.signal();
193+
let surfaces = { std::mem::take(&mut *self.surfaces.lock().unwrap()) };
194+
for (surface, _) in surfaces {
195+
let Ok(surface) = surface.upgrade() else {
196+
continue;
197+
};
198+
let Some(client) = surface.client() else {
199+
continue;
200+
};
201+
state.client_compositor_state(&client).blocker_cleared(state, dh);
202+
}
203+
}
204+
}
205+
206+
#[derive(Debug, Clone)]
207+
struct SurfaceBarrierBlocker {
208+
barrier: Barrier,
209+
ping: Ping,
210+
ready: Arc<AtomicBool>,
211+
surfaces: Arc<Mutex<HashMap<Weak<WlSurface>, bool>>>,
212+
}
213+
214+
impl Blocker for SurfaceBarrierBlocker {
215+
fn state(&self) -> BlockerState {
216+
self.barrier.state()
217+
}
218+
219+
fn kind(&self) -> BlockerKind {
220+
BlockerKind::Delayed
221+
}
222+
223+
fn notify(&self, surface: &WlSurface) {
224+
let mut surfaces = self.surfaces.lock().unwrap();
225+
surfaces
226+
.entry(surface.downgrade())
227+
.and_modify(|state| *state = true);
228+
if surfaces
229+
.iter()
230+
.all(|(surface, state)| *state || !surface.is_alive())
231+
{
232+
let signaled = self.ready.swap(true, std::sync::atomic::Ordering::Acquire);
233+
if !signaled {
234+
self.ping.ping();
235+
}
236+
}
237+
}
238+
}
239+
132240
#[derive(Default)]
133241
struct TransactionState {
134242
surfaces: Vec<(Weak<WlSurface>, Serial)>,

0 commit comments

Comments
 (0)