diff --git a/Cargo.toml b/Cargo.toml index b16ec22..03b4e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,8 @@ thiserror = "1.0.30" raw-window-handle = "0.4.2" [target.'cfg(target_os = "linux")'.dependencies] +tempfile = "3.3.0" +wayland-client = {version = "0.29", features = ["use_system_lib"], default_features = false} x11-dl = "2.19.1" [target.'cfg(target_os = "windows")'.dependencies] diff --git a/README.md b/README.md index cc32463..bebd703 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ For now, the priority for new platforms is: - AppKit ✅ (Thanks to [Seo Sanghyeon](https://github.com/sanxiyn)!) - Orbital ❌ - UiKit ❌ - - Wayland ❌ + - Wayland ✅ (Wayland support in winit is immature at the moment, so it might be wise to force X11 if you're using winit) - Web ❌ - Win32 ✅ - WinRt ❌ diff --git a/src/error.rs b/src/error.rs index 30f02bf..6e19074 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,3 +15,10 @@ pub enum SoftBufferError { #[error("Platform error")] PlatformError(Option, Option>) } + +pub(crate) fn unwrap(res: Result, str: &str) -> Result>{ + match res{ + Ok(t) => Ok(t), + Err(e) => Err(SoftBufferError::PlatformError(Some(str.into()), Some(Box::new(e)))) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ec97655..b27819e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,11 @@ mod win32; mod cg; #[cfg(target_os = "linux")] mod x11; +#[cfg(target_os = "linux")] +mod wayland; mod error; + pub use error::SoftBufferError; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; @@ -33,9 +36,11 @@ impl GraphicsContext { /// - Ensure that the passed object is valid to draw a 2D buffer to pub unsafe fn new(window: W) -> Result> { let raw_handle = window.raw_window_handle(); - let imple = match raw_handle { + let imple: Box = match raw_handle { #[cfg(target_os = "linux")] RawWindowHandle::Xlib(xlib_handle) => Box::new(x11::X11Impl::new(xlib_handle)?), + #[cfg(target_os = "linux")] + RawWindowHandle::Wayland(wayland_handle) => Box::new(wayland::WaylandImpl::new(wayland_handle)?), #[cfg(target_os = "windows")] RawWindowHandle::Win32(win32_handle) => Box::new(win32::Win32Impl::new(&win32_handle)?), #[cfg(target_os = "macos")] diff --git a/src/wayland.rs b/src/wayland.rs new file mode 100644 index 0000000..f10576b --- /dev/null +++ b/src/wayland.rs @@ -0,0 +1,69 @@ +use raw_window_handle::{HasRawWindowHandle, WaylandHandle}; +use tempfile::tempfile; +use wayland_client::{Display, sys::client::wl_display, GlobalManager, protocol::{wl_shm::WlShm, wl_buffer::WlBuffer, wl_surface::WlSurface}, Main, Proxy, EventQueue}; +use crate::{GraphicsContextImpl, SoftBufferError, error::unwrap}; +use std::{fs::File, os::unix::prelude::{AsRawFd, FileExt}, io::Write}; + +pub struct WaylandImpl { + _event_queue: EventQueue, + surface: WlSurface, + shm: Main, + tempfile: File, + buffer: Option +} + +struct WaylandBuffer{ + width: i32, + height: i32, + buffer: Main +} + +impl WaylandImpl { + + pub unsafe fn new(handle: WaylandHandle) -> Result> { + let display = Display::from_external_display(handle.display as *mut wl_display); + let mut event_queue = display.create_event_queue(); + let attached_display = (*display).clone().attach(event_queue.token()); + let globals = GlobalManager::new(&attached_display); + unwrap(event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()), "Failed to make round trip to server")?; + let shm = unwrap(globals.instantiate_exact::(1), "Failed to instantiate Wayland Shm")?; + let tempfile = unwrap(tempfile(), "Failed to create temporary file to store buffer.")?; + let surface = Proxy::from_c_ptr(handle.surface as _).into(); + Ok(Self{ + _event_queue: event_queue, + surface, shm, tempfile, + buffer: None + }) + } + + fn ensure_buffer_size(&mut self, width: i32, height: i32){ + if !self.check_buffer_size_equals(width, height){ + let pool = self.shm.create_pool(self.tempfile.as_raw_fd(), width*height*4); + let buffer = pool.create_buffer(0, width, height, width*4, wayland_client::protocol::wl_shm::Format::Xrgb8888); + self.buffer = Some(WaylandBuffer{ + width, + height, + buffer + }); + } + } + + fn check_buffer_size_equals(&self, width: i32, height: i32) -> bool{ + match &self.buffer{ + Some(buffer) => buffer.width == width && buffer.height == height, + None => false + } + } + +} + +impl GraphicsContextImpl for WaylandImpl { + unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + self.ensure_buffer_size(width as i32, height as i32); + let wayland_buffer = self.buffer.as_mut().unwrap(); + self.tempfile.write_at(std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()*4), 0).expect("Failed to write buffer to temporary file."); + self.tempfile.flush().expect("Failed to flush buffer to temporary file."); + self.surface.attach(Some(&wayland_buffer.buffer), 0, 0); + self.surface.commit(); + } +} \ No newline at end of file