From 7351d68814d20442b11953737883aa9f2ef05b4b Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 7 Dec 2022 11:27:36 -0700 Subject: [PATCH] Add Redox/Orbital support --- Cargo.toml | 3 ++ README.md | 2 +- src/lib.rs | 4 ++ src/orbital.rs | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/orbital.rs diff --git a/Cargo.toml b/Cargo.toml index 954d934..f0557fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,9 @@ wasm-bindgen = "0.2.78" version = "0.3.55" features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "ImageData", "Window"] +[target.'cfg(target_os = "redox")'.dependencies] +redox_syscall = "0.3" + [dev-dependencies] instant = "0.1.12" winit = "0.27.2" diff --git a/README.md b/README.md index 7c91089..8c4b006 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ For now, the priority for new platforms is: ✅: Present | ❌: Absent - AndroidNdk ❌ - AppKit ✅ (Thanks to [Seo Sanghyeon](https://github.com/sanxiyn) and [lunixbochs](https://github.com/lunixbochs)!) - - Orbital ❌ + - Orbital ✅ - UiKit ❌ - Wayland ✅ (Wayland support in winit is immature at the moment, so it might be wise to force X11 if you're using winit) - Web ✅ (Thanks to [Liamolucko](https://github.com/Liamolucko)!) diff --git a/src/lib.rs b/src/lib.rs index 8730629..f597386 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ mod x11; mod wayland; #[cfg(target_arch = "wasm32")] mod web; +#[cfg(target_os = "redox")] +mod orbital; mod error; @@ -52,6 +54,8 @@ impl GraphicsContext { (RawWindowHandle::AppKit(appkit_handle), _) => Box::new(cg::CGImpl::new(appkit_handle)?), #[cfg(target_arch = "wasm32")] (RawWindowHandle::Web(web_handle), _) => Box::new(web::WebImpl::new(web_handle)?), + #[cfg(target_os = "redox")] + (RawWindowHandle::Orbital(orbital_handle), _) => Box::new(orbital::OrbitalImpl::new(orbital_handle)?), (unimplemented_window_handle, unimplemented_display_handle) => return Err(SoftBufferError::UnsupportedPlatform { window, human_readable_window_platform_name: window_handle_type_name(&unimplemented_window_handle), diff --git a/src/orbital.rs b/src/orbital.rs new file mode 100644 index 0000000..76cbdb6 --- /dev/null +++ b/src/orbital.rs @@ -0,0 +1,108 @@ +use raw_window_handle::HasRawWindowHandle; +use raw_window_handle::OrbitalWindowHandle; +use std::{ + cmp, + slice, + str, +}; + +use crate::GraphicsContextImpl; +use crate::SoftBufferError; + +struct OrbitalMap { + address: usize, + size: 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 = syscall::fmap(fd, &syscall::Map { + offset: 0, + size, + flags: syscall::PROT_READ | syscall::PROT_WRITE, + address: 0, + })?; + + Ok(Self { address, size }) + } +} + +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, +} + +impl OrbitalImpl { + pub fn new(handle: OrbitalWindowHandle) -> Result> { + Ok(Self { handle }) + } +} + +impl GraphicsContextImpl for OrbitalImpl { + unsafe fn set_buffer(&mut self, buffer: &[u32], width_u16: u16, height_u16: u16) { + let window_fd = self.handle.window as usize; + + // Read the current width and size + let mut window_width = 0; + let mut window_height = 0; + { + let mut buf: [u8; 4096] = [0; 4096]; + let count = syscall::fpath(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); + } + } + + { + // Map window buffer + let window_map = OrbitalMap::new( + 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 = slice::from_raw_parts_mut( + window_map.address as *mut u32, + window_width * window_height + ); + + // Copy each line, cropping to fit + let width = width_u16 as usize; + let height = height_u16 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(window_fd).expect("failed to sync orbital window"); + } +}