⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/desktop/wayland/
surface.rs

1//! Wayland Surface
2//!
3//! Represents a renderable rectangular area. Each surface has pending and
4//! committed state (double-buffered protocol state) and tracks attached
5//! buffers, position, damage, and opaque regions.
6#![allow(dead_code)]
7
8use alloc::vec::Vec;
9
10use super::buffer::Buffer;
11use crate::error::KernelError;
12
13// ---------------------------------------------------------------------------
14// Damage region
15// ---------------------------------------------------------------------------
16
17/// A rectangular damage region on a surface (pixels that changed).
18#[derive(Debug, Clone, Copy)]
19pub struct DamageRect {
20    pub x: i32,
21    pub y: i32,
22    pub width: u32,
23    pub height: u32,
24}
25
26// ---------------------------------------------------------------------------
27// Surface state (pending / committed)
28// ---------------------------------------------------------------------------
29
30/// Per-surface state that is applied atomically on commit.
31#[derive(Debug, Clone)]
32pub struct SurfaceState {
33    /// Attached buffer (None = no buffer / transparent)
34    pub buffer: Option<Buffer>,
35    /// Buffer offset from surface origin
36    pub buffer_offset: (i32, i32),
37    /// Accumulated damage regions since last commit
38    pub damage: Vec<DamageRect>,
39    /// Opaque region hint (currently unused, reserved for Phase 6)
40    pub opaque: Vec<DamageRect>,
41    /// Input region (where the surface accepts pointer/touch)
42    pub input: Vec<DamageRect>,
43}
44
45impl SurfaceState {
46    fn new() -> Self {
47        Self {
48            buffer: None,
49            buffer_offset: (0, 0),
50            damage: Vec::new(),
51            opaque: Vec::new(),
52            input: Vec::new(),
53        }
54    }
55}
56
57// ---------------------------------------------------------------------------
58// Surface
59// ---------------------------------------------------------------------------
60
61/// A Wayland surface representing a renderable area.
62pub struct Surface {
63    /// Surface ID (Wayland object ID)
64    pub id: u32,
65    /// Committed (current) state -- what the compositor reads
66    pub committed: SurfaceState,
67    /// Pending state -- accumulated by attach/damage before commit
68    pub pending: SurfaceState,
69    /// Position in compositor coordinate space (set by shell role)
70    pub position: (i32, i32),
71    /// Committed size (derived from buffer dimensions)
72    pub size: (u32, u32),
73    /// Whether this surface has new content since last composite
74    pub dirty: bool,
75    /// Whether the surface is mapped (has a committed buffer and a role)
76    pub mapped: bool,
77    /// Owning client ID
78    pub client_id: u32,
79}
80
81impl Surface {
82    /// Create a new unmapped surface.
83    pub fn new(id: u32) -> Self {
84        Self {
85            id,
86            committed: SurfaceState::new(),
87            pending: SurfaceState::new(),
88            position: (0, 0),
89            size: (0, 0),
90            dirty: false,
91            mapped: false,
92            client_id: 0,
93        }
94    }
95
96    /// Create a new surface with a specific client ID.
97    pub fn with_client(id: u32, client_id: u32) -> Self {
98        let mut s = Self::new(id);
99        s.client_id = client_id;
100        s
101    }
102
103    /// Attach a buffer to the pending state (wl_surface.attach).
104    pub fn attach_buffer(&mut self, buffer: Buffer) {
105        self.pending.buffer = Some(buffer);
106    }
107
108    /// Attach with an explicit offset.
109    pub fn attach_buffer_at(&mut self, buffer: Buffer, dx: i32, dy: i32) {
110        self.pending.buffer_offset = (dx, dy);
111        self.pending.buffer = Some(buffer);
112    }
113
114    /// Mark a damage region on the pending state (wl_surface.damage).
115    pub fn damage(&mut self, x: i32, y: i32, width: u32, height: u32) {
116        self.pending.damage.push(DamageRect {
117            x,
118            y,
119            width,
120            height,
121        });
122    }
123
124    /// Mark the entire surface as damaged.
125    pub fn damage_full(&mut self) {
126        if let Some(ref buf) = self.pending.buffer {
127            self.pending.damage.push(DamageRect {
128                x: 0,
129                y: 0,
130                width: buf.width,
131                height: buf.height,
132            });
133        } else if self.size.0 > 0 && self.size.1 > 0 {
134            self.pending.damage.push(DamageRect {
135                x: 0,
136                y: 0,
137                width: self.size.0,
138                height: self.size.1,
139            });
140        }
141    }
142
143    /// Commit pending state to committed state (wl_surface.commit).
144    ///
145    /// This is the atomic state-swap that the Wayland protocol requires.
146    pub fn commit(&mut self) -> Result<(), KernelError> {
147        // Swap buffer
148        if self.pending.buffer.is_some() {
149            self.committed.buffer = self.pending.buffer.take();
150            self.committed.buffer_offset = self.pending.buffer_offset;
151
152            // Update size from committed buffer
153            if let Some(ref buf) = self.committed.buffer {
154                self.size = (buf.width, buf.height);
155                self.mapped = true;
156            }
157        }
158
159        // Merge damage
160        if !self.pending.damage.is_empty() {
161            self.committed.damage.clear();
162            core::mem::swap(&mut self.committed.damage, &mut self.pending.damage);
163            self.dirty = true;
164        }
165
166        // Clear pending damage after commit
167        self.pending.damage.clear();
168        self.pending.buffer_offset = (0, 0);
169
170        Ok(())
171    }
172
173    /// Check whether this surface has a committed buffer.
174    pub fn has_buffer(&self) -> bool {
175        self.committed.buffer.is_some()
176    }
177
178    /// Clear the dirty flag after the compositor has rendered this surface.
179    pub fn clear_dirty(&mut self) {
180        self.dirty = false;
181        self.committed.damage.clear();
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use crate::graphics::PixelFormat;
189
190    #[test]
191    fn test_surface_lifecycle() {
192        let mut surface = Surface::new(1);
193        assert!(!surface.mapped);
194        assert!(!surface.dirty);
195
196        let buf = Buffer::new(1, 640, 480, PixelFormat::Xrgb8888);
197        surface.attach_buffer(buf);
198        surface.damage(0, 0, 640, 480);
199        surface.commit().unwrap();
200
201        assert!(surface.mapped);
202        assert!(surface.dirty);
203        assert_eq!(surface.size, (640, 480));
204    }
205
206    #[test]
207    fn test_surface_clear_dirty() {
208        let mut surface = Surface::new(1);
209        let buf = Buffer::new(1, 100, 100, PixelFormat::Argb8888);
210        surface.attach_buffer(buf);
211        surface.damage(0, 0, 100, 100);
212        surface.commit().unwrap();
213        assert!(surface.dirty);
214
215        surface.clear_dirty();
216        assert!(!surface.dirty);
217    }
218}