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

veridian_kernel/desktop/wayland/
buffer.rs

1//! Wayland Buffer and SHM Pool Management
2//!
3//! Provides pixel buffer allocation backed by kernel-heap memory pools.
4//! Each `WlShmPool` owns a contiguous byte allocation from which individual
5//! `WlBuffer` objects are sub-allocated at arbitrary offsets. This mirrors
6//! the real Wayland wl_shm / wl_shm_pool / wl_buffer protocol objects.
7#![allow(dead_code)]
8
9use alloc::{collections::BTreeMap, vec, vec::Vec};
10
11use spin::Mutex;
12
13use crate::{error::KernelError, graphics::PixelFormat};
14
15// ---------------------------------------------------------------------------
16// WlShmPool -- shared memory pool
17// ---------------------------------------------------------------------------
18
19/// A shared memory pool that backs one or more buffers.
20///
21/// In a real Wayland compositor the pool would reference a client-provided
22/// file descriptor pointing to an mmap'd region. Here we allocate from the
23/// kernel heap as a stand-in, since user-space shared memory is not yet
24/// wired for graphics.
25pub struct WlShmPool {
26    /// Pool object ID (Wayland protocol ID)
27    pub id: u32,
28    /// Owning client ID
29    pub client_id: u32,
30    /// Backing byte storage
31    data: Vec<u8>,
32    /// Total size in bytes
33    pub size: usize,
34    /// Buffers sub-allocated from this pool (buffer_id -> WlBuffer)
35    buffers: BTreeMap<u32, WlBuffer>,
36    /// Next buffer ID within this pool
37    next_buffer_id: u32,
38}
39
40impl WlShmPool {
41    /// Create a new pool with `size` bytes of zeroed backing memory.
42    pub fn new(id: u32, client_id: u32, size: usize) -> Self {
43        Self {
44            id,
45            client_id,
46            data: vec![0u8; size],
47            size,
48            buffers: BTreeMap::new(),
49            next_buffer_id: 1,
50        }
51    }
52
53    /// Create a buffer that references a region within this pool.
54    ///
55    /// Arguments mirror wl_shm_pool.create_buffer:
56    ///   offset -- byte offset into the pool
57    ///   width, height -- dimensions in pixels
58    ///   stride -- bytes per row
59    ///   format -- pixel format code
60    pub fn create_buffer(
61        &mut self,
62        offset: u32,
63        width: u32,
64        height: u32,
65        stride: u32,
66        format: PixelFormat,
67    ) -> Result<u32, KernelError> {
68        // Validate that the described region fits inside the pool.
69        let end = offset as usize + (stride as usize) * (height as usize);
70        if end > self.size {
71            return Err(KernelError::InvalidArgument {
72                name: "buffer region",
73                value: "exceeds pool size",
74            });
75        }
76        if stride < width * format.bpp() {
77            return Err(KernelError::InvalidArgument {
78                name: "stride",
79                value: "smaller than row width",
80            });
81        }
82
83        let buf_id = self.next_buffer_id;
84        self.next_buffer_id += 1;
85
86        let buffer = WlBuffer {
87            id: buf_id,
88            pool_id: self.id,
89            offset,
90            width,
91            height,
92            stride,
93            format,
94            released: true, // initially available for client writes
95        };
96        self.buffers.insert(buf_id, buffer);
97        Ok(buf_id)
98    }
99
100    /// Get an immutable reference to a buffer.
101    pub fn get_buffer(&self, buffer_id: u32) -> Option<&WlBuffer> {
102        self.buffers.get(&buffer_id)
103    }
104
105    /// Remove a buffer from this pool.
106    pub fn destroy_buffer(&mut self, buffer_id: u32) -> bool {
107        self.buffers.remove(&buffer_id).is_some()
108    }
109
110    /// Read pixel data for a buffer from the pool backing store.
111    ///
112    /// Returns a slice of the pool data corresponding to the buffer's region.
113    pub fn read_buffer_pixels(&self, buffer_id: u32) -> Option<&[u8]> {
114        let buf = self.buffers.get(&buffer_id)?;
115        let start = buf.offset as usize;
116        let len = buf.stride as usize * buf.height as usize;
117        if start + len > self.data.len() {
118            return None;
119        }
120        Some(&self.data[start..start + len])
121    }
122
123    /// Write pixel data into the pool backing store at the buffer's offset.
124    ///
125    /// Used for testing and by kernel-side rendering that needs to fill a
126    /// buffer (e.g. a cursor image).
127    pub fn write_buffer_pixels(&mut self, buffer_id: u32, pixels: &[u8]) -> bool {
128        let buf = match self.buffers.get(&buffer_id) {
129            Some(b) => b,
130            None => return false,
131        };
132        let start = buf.offset as usize;
133        let len = buf.stride as usize * buf.height as usize;
134        if start + len > self.data.len() || pixels.len() < len {
135            return false;
136        }
137        self.data[start..start + len].copy_from_slice(&pixels[..len]);
138        true
139    }
140
141    /// Get raw access to the pool's backing memory (for direct pixel reads
142    /// by the compositor).
143    pub fn data(&self) -> &[u8] {
144        &self.data
145    }
146
147    /// Write raw bytes into the pool at a given offset.
148    ///
149    /// Used by the desktop renderer to populate background/window pixel data.
150    pub fn write_data(&mut self, offset: usize, data: &[u8]) {
151        let end = (offset + data.len()).min(self.data.len());
152        let src_len = end - offset;
153        self.data[offset..end].copy_from_slice(&data[..src_len]);
154    }
155
156    /// Resize the pool (wl_shm_pool.resize). Only growing is allowed.
157    pub fn resize(&mut self, new_size: usize) -> Result<(), KernelError> {
158        if new_size < self.size {
159            return Err(KernelError::InvalidArgument {
160                name: "pool size",
161                value: "cannot shrink",
162            });
163        }
164        self.data.resize(new_size, 0);
165        self.size = new_size;
166        Ok(())
167    }
168}
169
170// ---------------------------------------------------------------------------
171// WlBuffer -- sub-region of a pool
172// ---------------------------------------------------------------------------
173
174/// A buffer object referencing pixel data within a `WlShmPool`.
175#[derive(Debug, Clone)]
176pub struct WlBuffer {
177    /// Buffer object ID
178    pub id: u32,
179    /// Owning pool ID
180    pub pool_id: u32,
181    /// Byte offset into the pool
182    pub offset: u32,
183    /// Width in pixels
184    pub width: u32,
185    /// Height in pixels
186    pub height: u32,
187    /// Bytes per row
188    pub stride: u32,
189    /// Pixel format
190    pub format: PixelFormat,
191    /// Whether the compositor has released this buffer back to the client
192    pub released: bool,
193}
194
195impl WlBuffer {
196    /// Total byte size of pixel data for this buffer.
197    pub fn byte_size(&self) -> usize {
198        self.stride as usize * self.height as usize
199    }
200}
201
202// ---------------------------------------------------------------------------
203// Global SHM pool registry
204// ---------------------------------------------------------------------------
205
206/// Registry of all SHM pools, keyed by pool object ID.
207///
208/// Accessed from both the Wayland protocol dispatcher and the compositor
209/// rendering path, so we use a spin Mutex.
210static SHM_POOLS: Mutex<Option<BTreeMap<u32, WlShmPool>>> = Mutex::new(None);
211
212/// Initialize the SHM pool registry (called once during Wayland init).
213pub fn init_shm_pools() {
214    let mut pools = SHM_POOLS.lock();
215    if pools.is_none() {
216        *pools = Some(BTreeMap::new());
217    }
218}
219
220/// Register a new SHM pool.
221pub fn register_pool(pool: WlShmPool) -> u32 {
222    let id = pool.id;
223    let mut guard = SHM_POOLS.lock();
224    if let Some(ref mut pools) = *guard {
225        pools.insert(id, pool);
226    }
227    id
228}
229
230/// Execute a closure with mutable access to a specific pool.
231pub fn with_pool_mut<R, F: FnOnce(&mut WlShmPool) -> R>(pool_id: u32, f: F) -> Option<R> {
232    let mut guard = SHM_POOLS.lock();
233    guard
234        .as_mut()
235        .and_then(|pools| pools.get_mut(&pool_id).map(f))
236}
237
238/// Execute a closure with read-only access to a specific pool.
239pub fn with_pool<R, F: FnOnce(&WlShmPool) -> R>(pool_id: u32, f: F) -> Option<R> {
240    let guard = SHM_POOLS.lock();
241    guard.as_ref().and_then(|pools| pools.get(&pool_id).map(f))
242}
243
244/// Remove a pool from the registry.
245pub fn unregister_pool(pool_id: u32) -> Option<WlShmPool> {
246    let mut guard = SHM_POOLS.lock();
247    guard.as_mut().and_then(|pools| pools.remove(&pool_id))
248}
249
250// ---------------------------------------------------------------------------
251// Legacy compat: Buffer type alias used by surface.rs
252// ---------------------------------------------------------------------------
253
254/// Legacy buffer struct retained for surface.rs compatibility.
255/// New code should use `WlBuffer` + pool references.
256#[derive(Debug, Clone)]
257pub struct Buffer {
258    /// Buffer ID
259    pub id: u32,
260    /// Width in pixels
261    pub width: u32,
262    /// Height in pixels
263    pub height: u32,
264    /// Stride (bytes per row)
265    pub stride: u32,
266    /// Pixel format
267    pub format: PixelFormat,
268    /// Pool ID that owns this buffer's pixel data
269    pub pool_id: u32,
270    /// Buffer ID within the pool
271    pub pool_buffer_id: u32,
272}
273
274impl Buffer {
275    /// Create a new buffer descriptor.
276    pub fn new(id: u32, width: u32, height: u32, format: PixelFormat) -> Self {
277        let stride = width * format.bpp();
278        Self {
279            id,
280            width,
281            height,
282            stride,
283            format,
284            pool_id: 0,
285            pool_buffer_id: 0,
286        }
287    }
288
289    /// Create a buffer linked to a pool.
290    pub fn from_pool(
291        id: u32,
292        pool_id: u32,
293        pool_buffer_id: u32,
294        width: u32,
295        height: u32,
296        stride: u32,
297        format: PixelFormat,
298    ) -> Self {
299        Self {
300            id,
301            width,
302            height,
303            stride,
304            format,
305            pool_id,
306            pool_buffer_id,
307        }
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_pixel_format_bpp() {
317        assert_eq!(PixelFormat::Argb8888.bpp(), 4);
318        assert_eq!(PixelFormat::Xrgb8888.bpp(), 4);
319        assert_eq!(PixelFormat::Rgb565.bpp(), 2);
320    }
321
322    #[test]
323    fn test_pool_create_buffer() {
324        let mut pool = WlShmPool::new(1, 1, 1024 * 768 * 4);
325        let buf_id = pool
326            .create_buffer(0, 1024, 768, 1024 * 4, PixelFormat::Xrgb8888)
327            .unwrap();
328        assert_eq!(buf_id, 1);
329        let buf = pool.get_buffer(buf_id).unwrap();
330        assert_eq!(buf.width, 1024);
331        assert_eq!(buf.height, 768);
332    }
333
334    #[test]
335    fn test_pool_buffer_out_of_bounds() {
336        let mut pool = WlShmPool::new(1, 1, 100);
337        // Request buffer larger than pool
338        let result = pool.create_buffer(0, 100, 100, 400, PixelFormat::Argb8888);
339        assert!(result.is_err());
340    }
341
342    #[test]
343    fn test_pool_write_read_pixels() {
344        let mut pool = WlShmPool::new(1, 1, 16);
345        let buf_id = pool
346            .create_buffer(0, 2, 2, 8, PixelFormat::Xrgb8888)
347            .unwrap();
348        let pixels = [0xFFu8; 16];
349        assert!(pool.write_buffer_pixels(buf_id, &pixels));
350        let read = pool.read_buffer_pixels(buf_id).unwrap();
351        assert_eq!(read, &pixels[..]);
352    }
353
354    #[test]
355    fn test_format_from_wl() {
356        assert_eq!(PixelFormat::from_wl_format(0), Some(PixelFormat::Argb8888));
357        assert_eq!(PixelFormat::from_wl_format(1), Some(PixelFormat::Xrgb8888));
358        assert_eq!(PixelFormat::from_wl_format(999), None);
359    }
360}