|
| 1 | +use raw_window_handle::HasRawWindowHandle; |
| 2 | +use raw_window_handle::OrbitalWindowHandle; |
| 3 | +use std::{ |
| 4 | + cmp, |
| 5 | + slice, |
| 6 | + str, |
| 7 | +}; |
| 8 | + |
| 9 | +use crate::GraphicsContextImpl; |
| 10 | +use crate::SoftBufferError; |
| 11 | + |
| 12 | +struct OrbitalMap { |
| 13 | + address: usize, |
| 14 | + size: usize, |
| 15 | +} |
| 16 | + |
| 17 | +impl OrbitalMap { |
| 18 | + unsafe fn new(fd: usize, size_unaligned: usize) -> syscall::Result<Self> { |
| 19 | + // Page align size |
| 20 | + let pages = (size_unaligned + syscall::PAGE_SIZE - 1) / syscall::PAGE_SIZE; |
| 21 | + let size = pages * syscall::PAGE_SIZE; |
| 22 | + |
| 23 | + // Map window buffer |
| 24 | + let address = syscall::fmap(fd, &syscall::Map { |
| 25 | + offset: 0, |
| 26 | + size, |
| 27 | + flags: syscall::PROT_READ | syscall::PROT_WRITE, |
| 28 | + address: 0, |
| 29 | + })?; |
| 30 | + |
| 31 | + Ok(Self { address, size }) |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +impl Drop for OrbitalMap { |
| 36 | + fn drop(&mut self) { |
| 37 | + unsafe { |
| 38 | + // Unmap window buffer on drop |
| 39 | + syscall::funmap(self.address, self.size) |
| 40 | + .expect("failed to unmap orbital window"); |
| 41 | + } |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +pub struct OrbitalImpl { |
| 46 | + handle: OrbitalWindowHandle, |
| 47 | +} |
| 48 | + |
| 49 | +impl OrbitalImpl { |
| 50 | + pub fn new<W: HasRawWindowHandle>(handle: OrbitalWindowHandle) -> Result<Self, SoftBufferError<W>> { |
| 51 | + Ok(Self { handle }) |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +impl GraphicsContextImpl for OrbitalImpl { |
| 56 | + unsafe fn set_buffer(&mut self, buffer: &[u32], width_u16: u16, height_u16: u16) { |
| 57 | + let window_fd = self.handle.window as usize; |
| 58 | + |
| 59 | + // Read the current width and size |
| 60 | + let mut window_width = 0; |
| 61 | + let mut window_height = 0; |
| 62 | + { |
| 63 | + let mut buf: [u8; 4096] = [0; 4096]; |
| 64 | + let count = syscall::fpath(window_fd, &mut buf).unwrap(); |
| 65 | + let path = str::from_utf8(&buf[..count]).unwrap(); |
| 66 | + // orbital:/x/y/w/h/t |
| 67 | + let mut parts = path.split('/').skip(3); |
| 68 | + if let Some(w) = parts.next() { |
| 69 | + window_width = w.parse::<usize>().unwrap_or(0); |
| 70 | + } |
| 71 | + if let Some(h) = parts.next() { |
| 72 | + window_height = h.parse::<usize>().unwrap_or(0); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + { |
| 77 | + // Map window buffer |
| 78 | + let window_map = OrbitalMap::new( |
| 79 | + window_fd, |
| 80 | + window_width * window_height * 4 |
| 81 | + ).expect("failed to map orbital window"); |
| 82 | + |
| 83 | + // Window buffer is u32 color data in 0xAABBGGRR format |
| 84 | + let window_data = slice::from_raw_parts_mut( |
| 85 | + window_map.address as *mut u32, |
| 86 | + window_width * window_height |
| 87 | + ); |
| 88 | + |
| 89 | + // Copy each line, cropping to fit |
| 90 | + let width = width_u16 as usize; |
| 91 | + let height = height_u16 as usize; |
| 92 | + let min_width = cmp::min(width, window_width); |
| 93 | + let min_height = cmp::min(height, window_height); |
| 94 | + for y in 0..min_height { |
| 95 | + let offset_buffer = y * width; |
| 96 | + let offset_data = y * window_width; |
| 97 | + window_data[offset_data..offset_data + min_width].copy_from_slice( |
| 98 | + &buffer[offset_buffer..offset_buffer + min_width] |
| 99 | + ); |
| 100 | + } |
| 101 | + |
| 102 | + // Window buffer map is dropped here |
| 103 | + } |
| 104 | + |
| 105 | + // Tell orbital to show the latest window data |
| 106 | + syscall::fsync(window_fd).expect("failed to sync orbital window"); |
| 107 | + } |
| 108 | +} |
0 commit comments