use raw_window_handle::OrbitalWindowHandle; use std::{cmp, num::NonZeroU32, slice, str}; use crate::{Rect, SoftBufferError}; struct OrbitalMap { address: usize, size: usize, size_unaligned: usize, } impl OrbitalMap { unsafe fn new(fd: usize, size_unaligned: usize) -> syscall::Result { // Page align size let pages = (size_unaligned + syscall::PAGE_SIZE - 1) / syscall::PAGE_SIZE; let size = pages * syscall::PAGE_SIZE; // Map window buffer let address = unsafe { syscall::fmap( fd, &syscall::Map { offset: 0, size, flags: syscall::PROT_READ | syscall::PROT_WRITE, address: 0, }, )? }; Ok(Self { address, size, size_unaligned, }) } unsafe fn data(&self) -> &[u32] { unsafe { slice::from_raw_parts(self.address as *const u32, self.size_unaligned / 4) } } unsafe fn data_mut(&self) -> &mut [u32] { unsafe { slice::from_raw_parts_mut(self.address as *mut u32, self.size_unaligned / 4) } } } impl Drop for OrbitalMap { fn drop(&mut self) { unsafe { // Unmap window buffer on drop syscall::funmap(self.address, self.size).expect("failed to unmap orbital window"); } } } pub struct OrbitalImpl { handle: OrbitalWindowHandle, width: u32, height: u32, presented: bool, } impl OrbitalImpl { pub fn new(handle: OrbitalWindowHandle) -> Result { Ok(Self { handle, width: 0, height: 0, presented: false, }) } pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { let width = width.get(); let height = height.get(); if width != self.width && height != self.height { self.presented = false; self.width = width; self.height = height; } Ok(()) } fn window_fd(&self) -> usize { self.handle.window as usize } // Read the current width and size fn window_size(&self) -> (usize, usize) { let mut window_width = 0; let mut window_height = 0; let mut buf: [u8; 4096] = [0; 4096]; let count = syscall::fpath(self.window_fd(), &mut buf).unwrap(); let path = str::from_utf8(&buf[..count]).unwrap(); // orbital:/x/y/w/h/t let mut parts = path.split('/').skip(3); if let Some(w) = parts.next() { window_width = w.parse::().unwrap_or(0); } if let Some(h) = parts.next() { window_height = h.parse::().unwrap_or(0); } (window_width, window_height) } pub fn buffer_mut(&mut self) -> Result { let (window_width, window_height) = self.window_size(); let pixels = if self.width as usize == window_width && self.height as usize == window_height { Pixels::Mapping( unsafe { OrbitalMap::new(self.window_fd(), window_width * window_height * 4) } .expect("failed to map orbital window"), ) } else { Pixels::Buffer(vec![0; self.width as usize * self.height as usize]) }; Ok(BufferImpl { imp: self, pixels }) } fn set_buffer(&self, buffer: &[u32], width_u32: u32, height_u32: u32) { // Read the current width and size let (window_width, window_height) = self.window_size(); { // Map window buffer let window_map = unsafe { OrbitalMap::new(self.window_fd(), window_width * window_height * 4) } .expect("failed to map orbital window"); // Window buffer is u32 color data in 0xAABBGGRR format let window_data = unsafe { window_map.data_mut() }; // Copy each line, cropping to fit let width = width_u32 as usize; let height = height_u32 as usize; let min_width = cmp::min(width, window_width); let min_height = cmp::min(height, window_height); for y in 0..min_height { let offset_buffer = y * width; let offset_data = y * window_width; window_data[offset_data..offset_data + min_width] .copy_from_slice(&buffer[offset_buffer..offset_buffer + min_width]); } // Window buffer map is dropped here } // Tell orbital to show the latest window data syscall::fsync(self.window_fd()).expect("failed to sync orbital window"); } /// Fetch the buffer from the window. pub fn fetch(&mut self) -> Result, SoftBufferError> { Err(SoftBufferError::Unimplemented) } } enum Pixels { Mapping(OrbitalMap), Buffer(Vec), } pub struct BufferImpl<'a> { imp: &'a mut OrbitalImpl, pixels: Pixels, } impl<'a> BufferImpl<'a> { #[inline] pub fn pixels(&self) -> &[u32] { match &self.pixels { Pixels::Mapping(mapping) => unsafe { mapping.data() }, Pixels::Buffer(buffer) => buffer, } } #[inline] pub fn pixels_mut(&mut self) -> &mut [u32] { match &mut self.pixels { Pixels::Mapping(mapping) => unsafe { mapping.data_mut() }, Pixels::Buffer(buffer) => buffer, } } pub fn age(&self) -> u8 { match self.pixels { Pixels::Mapping(_) if self.imp.presented => 1, _ => 0, } } pub fn present(self) -> Result<(), SoftBufferError> { match self.pixels { Pixels::Mapping(mapping) => { drop(mapping); syscall::fsync(self.imp.window_fd()).expect("failed to sync orbital window"); self.imp.presented = true; } Pixels::Buffer(buffer) => { self.imp .set_buffer(&buffer, self.imp.width, self.imp.height); } } Ok(()) } pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> { self.present() } }