Skip to content

Commit 3f660ed

Browse files
committed
wayland: add a (multi)surface barrier
1 parent bcf197a commit 3f660ed

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-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: 111 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,114 @@ 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+
#[allow(clippy::mutable_key_type)]
194+
let surfaces = { std::mem::take(&mut *self.surfaces.lock().unwrap()) };
195+
for (surface, _) in surfaces {
196+
let Ok(surface) = surface.upgrade() else {
197+
continue;
198+
};
199+
let Some(client) = surface.client() else {
200+
continue;
201+
};
202+
state.client_compositor_state(&client).blocker_cleared(state, dh);
203+
}
204+
}
205+
}
206+
207+
#[derive(Debug, Clone)]
208+
struct SurfaceBarrierBlocker {
209+
barrier: Barrier,
210+
ping: Ping,
211+
ready: Arc<AtomicBool>,
212+
surfaces: Arc<Mutex<HashMap<Weak<WlSurface>, bool>>>,
213+
}
214+
215+
impl Blocker for SurfaceBarrierBlocker {
216+
fn state(&self) -> BlockerState {
217+
self.barrier.state()
218+
}
219+
220+
fn kind(&self) -> BlockerKind {
221+
BlockerKind::Delayed
222+
}
223+
224+
fn notify(&self, surface: &WlSurface) {
225+
let mut surfaces = self.surfaces.lock().unwrap();
226+
surfaces
227+
.entry(surface.downgrade())
228+
.and_modify(|state| *state = true);
229+
if surfaces
230+
.iter()
231+
.all(|(surface, state)| *state || !surface.is_alive())
232+
{
233+
let signaled = self.ready.swap(true, std::sync::atomic::Ordering::Acquire);
234+
if !signaled {
235+
self.ping.ping();
236+
}
237+
}
238+
}
239+
}
240+
132241
#[derive(Default)]
133242
struct TransactionState {
134243
surfaces: Vec<(Weak<WlSurface>, Serial)>,

0 commit comments

Comments
 (0)