|
39 | 39 | // but will be once proper transaction & blockers support is
|
40 | 40 | // added to smithay
|
41 | 41 | use std::{
|
42 |
| - collections::HashSet, |
| 42 | + collections::{HashMap, HashSet}, |
43 | 43 | fmt,
|
44 | 44 | sync::{atomic::AtomicBool, Arc, Mutex},
|
45 | 45 | };
|
46 | 46 |
|
| 47 | +use calloop::ping::Ping; |
47 | 48 | use wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource, Weak};
|
48 | 49 |
|
49 | 50 | use crate::utils::Serial;
|
50 | 51 |
|
51 |
| -use super::{tree::PrivateSurfaceData, CompositorHandler}; |
| 52 | +use super::{add_blocker, tree::PrivateSurfaceData, CompositorHandler}; |
52 | 53 |
|
53 | 54 | /// Kind for a [`Blocker`]
|
54 | 55 | #[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
@@ -129,6 +130,113 @@ impl Blocker for Barrier {
|
129 | 130 | }
|
130 | 131 | }
|
131 | 132 |
|
| 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 | + |
132 | 240 | #[derive(Default)]
|
133 | 241 | struct TransactionState {
|
134 | 242 | surfaces: Vec<(Weak<WlSurface>, Serial)>,
|
|
0 commit comments