|
| 1 | +use super::surface::{Surface, SurfaceRole}; |
| 2 | +use crate::core::Id; |
| 3 | +use crate::nodes::items::panel::{ChildInfo, Geometry, SurfaceId}; |
| 4 | +use crate::wayland::util::{BufferedState, SurfaceCommitAwareBuffer}; |
| 5 | +use crate::wayland::{WaylandError, WaylandResult}; |
| 6 | +use mint::Vector2; |
| 7 | +use parking_lot::Mutex; |
| 8 | +use rand::Rng; |
| 9 | +use std::sync::Arc; |
| 10 | +use std::sync::atomic::{AtomicBool, Ordering}; |
| 11 | +use waynest::ObjectId; |
| 12 | +use waynest_protocols::server::core::wayland::wl_subcompositor::{self, WlSubcompositor}; |
| 13 | +use waynest_protocols::server::core::wayland::wl_subsurface::WlSubsurface; |
| 14 | +use waynest_server::{Client as _, RequestDispatcher}; |
| 15 | + |
| 16 | +#[derive(Debug, waynest_server::RequestDispatcher)] |
| 17 | +#[waynest(error = WaylandError, connection = crate::wayland::Client)] |
| 18 | +pub struct Subcompositor; |
| 19 | + |
| 20 | +impl WlSubcompositor for Subcompositor { |
| 21 | + type Connection = crate::wayland::Client; |
| 22 | + |
| 23 | + /// https://wayland.app/protocols/wayland#wl_subcompositor:request:destroy |
| 24 | + async fn destroy( |
| 25 | + &self, |
| 26 | + client: &mut Self::Connection, |
| 27 | + sender_id: ObjectId, |
| 28 | + ) -> WaylandResult<()> { |
| 29 | + client.remove(sender_id); |
| 30 | + Ok(()) |
| 31 | + } |
| 32 | + |
| 33 | + /// https://wayland.app/protocols/wayland#wl_subcompositor:request:get_subsurface |
| 34 | + async fn get_subsurface( |
| 35 | + &self, |
| 36 | + client: &mut Self::Connection, |
| 37 | + _sender_id: ObjectId, |
| 38 | + id: ObjectId, |
| 39 | + surface_id: ObjectId, |
| 40 | + parent_id: ObjectId, |
| 41 | + ) -> WaylandResult<()> { |
| 42 | + let Some(parent) = client.get::<Surface>(parent_id) else { |
| 43 | + return Err(WaylandError::Fatal { |
| 44 | + object_id: parent_id, |
| 45 | + code: wl_subcompositor::Error::BadSurface as u32, |
| 46 | + message: "Parent surface does not exist", |
| 47 | + }); |
| 48 | + }; |
| 49 | + |
| 50 | + let Some(surface) = client.get::<Surface>(surface_id) else { |
| 51 | + return Err(WaylandError::Fatal { |
| 52 | + object_id: surface_id, |
| 53 | + code: wl_subcompositor::Error::BadSurface as u32, |
| 54 | + message: "Surface does not exist", |
| 55 | + }); |
| 56 | + }; |
| 57 | + |
| 58 | + // Set the subsurface role |
| 59 | + surface |
| 60 | + .try_set_role(SurfaceRole::Subsurface, wl_subcompositor::Error::BadSurface) |
| 61 | + .await?; |
| 62 | + |
| 63 | + // Create the subsurface |
| 64 | + let subsurface = Arc::new(Subsurface::new(id, surface.clone(), parent.clone())); |
| 65 | + client.insert_raw(id, subsurface.clone())?; |
| 66 | + |
| 67 | + // Set up commit handler and register child |
| 68 | + subsurface.setup(); |
| 69 | + |
| 70 | + Ok(()) |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +#[derive(Debug, Clone, Copy, PartialEq)] |
| 75 | +struct SubsurfaceState { |
| 76 | + position: (i32, i32), |
| 77 | + z_order: i32, |
| 78 | +} |
| 79 | + |
| 80 | +impl Default for SubsurfaceState { |
| 81 | + fn default() -> Self { |
| 82 | + Self { |
| 83 | + position: (0, 0), |
| 84 | + z_order: 0, // Initially below parent (parent is 0) |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | +impl BufferedState for SubsurfaceState { |
| 89 | + fn apply(&mut self, pending: &mut Self) { |
| 90 | + *self = pending.clone(); |
| 91 | + } |
| 92 | + |
| 93 | + fn get_initial_pending(&self) -> Self { |
| 94 | + self.clone() |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +#[derive(Debug, RequestDispatcher)] |
| 99 | +#[waynest(error = WaylandError, connection = crate::wayland::Client)] |
| 100 | +pub struct Subsurface { |
| 101 | + id: ObjectId, |
| 102 | + surface: Arc<Surface>, |
| 103 | + state: Arc<Mutex<SurfaceCommitAwareBuffer<SubsurfaceState>>>, |
| 104 | + child_id: Mutex<Option<Id>>, |
| 105 | + is_sync: AtomicBool, |
| 106 | +} |
| 107 | + |
| 108 | +impl Subsurface { |
| 109 | + pub fn new(id: ObjectId, surface: Arc<Surface>, parent: Arc<Surface>) -> Self { |
| 110 | + let child_id = Id(rand::rng().random()); |
| 111 | + let _ = surface.surface_id.set(SurfaceId::Child(child_id)); |
| 112 | + surface.set_parent(&parent); |
| 113 | + |
| 114 | + Self { |
| 115 | + id, |
| 116 | + state: SurfaceCommitAwareBuffer::new(SubsurfaceState::default(), &surface), |
| 117 | + surface, |
| 118 | + child_id: Mutex::new(Some(child_id)), |
| 119 | + is_sync: AtomicBool::new(true), // Subsurfaces start in sync mode |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + /// Check if this subsurface is effectively synchronized |
| 124 | + /// Per spec: "even if a sub-surface is set to desynchronized, |
| 125 | + /// a parent sub-surface may override it to behave as synchronized" |
| 126 | + fn is_effectively_sync(&self) -> bool { |
| 127 | + if !self.is_sync.load(Ordering::Acquire) { |
| 128 | + // We're desync, but check if parent is a synchronized subsurface |
| 129 | + if let Some(parent) = self.surface.parent() { |
| 130 | + if parent.role.get() == Some(&SurfaceRole::Subsurface) { |
| 131 | + // Parent is a subsurface - we inherit synchronized behavior |
| 132 | + // TODO: Could walk the chain recursively for perfect correctness |
| 133 | + return true; |
| 134 | + } |
| 135 | + } |
| 136 | + return false; |
| 137 | + } |
| 138 | + true |
| 139 | + } |
| 140 | + |
| 141 | + fn setup(self: &Arc<Self>) { |
| 142 | + // Set up commit filter to control when surface state is applied |
| 143 | + let subsurface_weak = Arc::downgrade(self); |
| 144 | + self.surface.set_parent_syncronized_filter(move || { |
| 145 | + let Some(subsurface) = subsurface_weak.upgrade() else { |
| 146 | + return true; // Subsurface gone, allow commit |
| 147 | + }; |
| 148 | + |
| 149 | + subsurface.is_effectively_sync() |
| 150 | + }); |
| 151 | + |
| 152 | + // First commit: add child when buffer is ready |
| 153 | + let subsurface_weak = Arc::downgrade(self); |
| 154 | + self.surface |
| 155 | + .add_updated_current_state_handler(move |surface| { |
| 156 | + let Some(subsurface) = subsurface_weak.upgrade() else { |
| 157 | + return true; |
| 158 | + }; |
| 159 | + let Some(parent) = subsurface.surface.parent() else { |
| 160 | + return true; |
| 161 | + }; |
| 162 | + let Some(panel_item) = parent.panel_item.lock().upgrade() else { |
| 163 | + return true; |
| 164 | + }; |
| 165 | + |
| 166 | + if surface.currently_has_valid_buffer() { |
| 167 | + *surface.panel_item.lock() = Arc::downgrade(&panel_item); |
| 168 | + let info = subsurface.create_child_info(surface.current_buffer_size()); |
| 169 | + panel_item.backend.add_child(&subsurface.surface, info); |
| 170 | + return false; // Remove handler after adding child once |
| 171 | + } |
| 172 | + true |
| 173 | + }); |
| 174 | + let subsurface_weak = Arc::downgrade(self); |
| 175 | + self.surface.add_commit_handler(move |_| { |
| 176 | + let Some(subsurface) = subsurface_weak.upgrade() else { |
| 177 | + return true; |
| 178 | + }; |
| 179 | + subsurface.state.lock().apply(); |
| 180 | + true |
| 181 | + }); |
| 182 | + // update subsurface geometry |
| 183 | + let subsurface_weak = Arc::downgrade(self); |
| 184 | + self.surface.add_updated_current_state_handler(move |_| { |
| 185 | + let Some(subsurface) = subsurface_weak.upgrade() else { |
| 186 | + return true; |
| 187 | + }; |
| 188 | + let surface = subsurface.surface.clone(); |
| 189 | + |
| 190 | + if surface.currently_has_valid_buffer() { |
| 191 | + if let Some(panel_item) = surface.panel_item.lock().upgrade() { |
| 192 | + let state = subsurface.state.lock(); |
| 193 | + let subsurface_state = *state.current(); |
| 194 | + drop(state); |
| 195 | + let size = surface |
| 196 | + .current_buffer_size() |
| 197 | + .map(|b| [b.x as u32, b.y as u32].into()) |
| 198 | + .unwrap_or([0; 2].into()); |
| 199 | + |
| 200 | + tracing::debug!("Updating backend after cached state apply: size={:?}", size); |
| 201 | + |
| 202 | + let geometry = Geometry { |
| 203 | + origin: [subsurface_state.position.0, subsurface_state.position.1].into(), |
| 204 | + size, |
| 205 | + }; |
| 206 | + panel_item.backend.reposition_child(&surface, geometry); |
| 207 | + panel_item |
| 208 | + .backend |
| 209 | + .update_child_z_order(&surface, subsurface_state.z_order); |
| 210 | + } |
| 211 | + } |
| 212 | + true |
| 213 | + }); |
| 214 | + } |
| 215 | + |
| 216 | + fn create_child_info(&self, buffer_size: Option<Vector2<usize>>) -> ChildInfo { |
| 217 | + let state = self.state.lock(); |
| 218 | + |
| 219 | + let size = buffer_size |
| 220 | + .map(|b| [b.x as u32, b.y as u32].into()) |
| 221 | + .unwrap_or([0; 2].into()); |
| 222 | + |
| 223 | + // Determine parent surface ID |
| 224 | + let parent_surface_id = self |
| 225 | + .surface |
| 226 | + .parent() |
| 227 | + .and_then(|p| p.surface_id.get().cloned()) |
| 228 | + .unwrap_or(SurfaceId::Toplevel(())); |
| 229 | + |
| 230 | + ChildInfo { |
| 231 | + id: self.child_id.lock().unwrap(), |
| 232 | + parent: parent_surface_id, |
| 233 | + geometry: Geometry { |
| 234 | + origin: [state.current().position.0, state.current().position.1].into(), |
| 235 | + size, |
| 236 | + }, |
| 237 | + z_order: state.current().z_order, |
| 238 | + receives_input: true, |
| 239 | + } |
| 240 | + } |
| 241 | +} |
| 242 | + |
| 243 | +impl WlSubsurface for Subsurface { |
| 244 | + type Connection = crate::wayland::Client; |
| 245 | + |
| 246 | + /// https://wayland.app/protocols/wayland#wl_subsurface:request:destroy |
| 247 | + async fn destroy( |
| 248 | + &self, |
| 249 | + client: &mut Self::Connection, |
| 250 | + _sender_id: ObjectId, |
| 251 | + ) -> WaylandResult<()> { |
| 252 | + // Remove the child from the parent's backend |
| 253 | + if let Some(parent) = self.surface.parent() { |
| 254 | + let Some(panel_item) = parent.panel_item.lock().upgrade() else { |
| 255 | + client.remove(self.id); |
| 256 | + return Ok(()); |
| 257 | + }; |
| 258 | + panel_item.backend.remove_child(&self.surface); |
| 259 | + } |
| 260 | + |
| 261 | + // Clear the commit filter |
| 262 | + self.surface.clear_parent_syncronized_filter(); |
| 263 | + |
| 264 | + client.remove(self.id); |
| 265 | + Ok(()) |
| 266 | + } |
| 267 | + |
| 268 | + /// https://wayland.app/protocols/wayland#wl_subsurface:request:set_position |
| 269 | + async fn set_position( |
| 270 | + &self, |
| 271 | + _client: &mut Self::Connection, |
| 272 | + _sender_id: ObjectId, |
| 273 | + x: i32, |
| 274 | + y: i32, |
| 275 | + ) -> WaylandResult<()> { |
| 276 | + self.state.lock().pending.position = (x, y); |
| 277 | + Ok(()) |
| 278 | + } |
| 279 | + |
| 280 | + /// https://wayland.app/protocols/wayland#wl_subsurface:request:place_above |
| 281 | + async fn place_above( |
| 282 | + &self, |
| 283 | + client: &mut Self::Connection, |
| 284 | + _sender_id: ObjectId, |
| 285 | + sibling: ObjectId, |
| 286 | + ) -> WaylandResult<()> { |
| 287 | + // Get the sibling's z_order |
| 288 | + let sibling_z_order = if let Some(sibling_surface) = client.get::<Surface>(sibling) |
| 289 | + && let Some(SurfaceId::Child(sibling_id)) = sibling_surface.surface_id.get() |
| 290 | + && let Some(parent) = self.surface.parent() |
| 291 | + && let Some(panel_item) = parent.panel_item.lock().upgrade() |
| 292 | + && let Some(child_entry) = panel_item.backend.children.get(sibling_id) |
| 293 | + { |
| 294 | + child_entry.1.z_order |
| 295 | + } else { |
| 296 | + 0 |
| 297 | + }; |
| 298 | + |
| 299 | + // Place this subsurface one level above the sibling |
| 300 | + self.state.lock().pending.z_order = sibling_z_order + 1; |
| 301 | + Ok(()) |
| 302 | + } |
| 303 | + |
| 304 | + /// https://wayland.app/protocols/wayland#wl_subsurface:request:place_below |
| 305 | + async fn place_below( |
| 306 | + &self, |
| 307 | + client: &mut Self::Connection, |
| 308 | + _sender_id: ObjectId, |
| 309 | + sibling: ObjectId, |
| 310 | + ) -> WaylandResult<()> { |
| 311 | + // Get the sibling's z_order |
| 312 | + let sibling_z_order = if let Some(sibling_surface) = client.get::<Surface>(sibling) |
| 313 | + && let Some(SurfaceId::Child(sibling_id)) = sibling_surface.surface_id.get() |
| 314 | + && let Some(parent) = self.surface.parent() |
| 315 | + && let Some(panel_item) = parent.panel_item.lock().upgrade() |
| 316 | + && let Some(child_entry) = panel_item.backend.children.get(sibling_id) |
| 317 | + { |
| 318 | + child_entry.1.z_order |
| 319 | + } else { |
| 320 | + 0 |
| 321 | + }; |
| 322 | + // Place this subsurface one level below the sibling |
| 323 | + self.state.lock().pending.z_order = sibling_z_order - 1; |
| 324 | + Ok(()) |
| 325 | + } |
| 326 | + |
| 327 | + /// https://wayland.app/protocols/wayland#wl_subsurface:request:set_sync |
| 328 | + async fn set_sync( |
| 329 | + &self, |
| 330 | + _client: &mut Self::Connection, |
| 331 | + _sender_id: ObjectId, |
| 332 | + ) -> WaylandResult<()> { |
| 333 | + self.is_sync.store(true, Ordering::Release); |
| 334 | + Ok(()) |
| 335 | + } |
| 336 | + |
| 337 | + /// https://wayland.app/protocols/wayland#wl_subsurface:request:set_desync |
| 338 | + async fn set_desync( |
| 339 | + &self, |
| 340 | + _client: &mut Self::Connection, |
| 341 | + _sender_id: ObjectId, |
| 342 | + ) -> WaylandResult<()> { |
| 343 | + let was_sync = self.is_sync.swap(false, Ordering::AcqRel); |
| 344 | + |
| 345 | + if was_sync { |
| 346 | + // TODO: figure out if this should be recursive or only for this surface |
| 347 | + self.surface.update_current_state_recursive(); |
| 348 | + } |
| 349 | + |
| 350 | + Ok(()) |
| 351 | + } |
| 352 | +} |
0 commit comments