diff --git a/Cargo.toml b/Cargo.toml index f0557fe..b957e88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,9 @@ thiserror = "1.0.30" raw-window-handle = "0.5.0" [target.'cfg(target_os = "linux")'.dependencies] -tempfile = "3.3.0" -wayland-client = {version = "0.29", features = ["use_system_lib"], default_features = false} +nix = "0.26.1" +wayland-backend = {version = "0.1.0-beta.14", features = ["client_system"]} +wayland-client = {version = "0.30.0-beta.14"} x11-dl = "2.19.1" [target.'cfg(target_os = "windows")'.dependencies.winapi] diff --git a/src/wayland.rs b/src/wayland.rs index db78275..32890b5 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -1,74 +1,140 @@ +use crate::{error::unwrap, GraphicsContextImpl, SoftBufferError}; +use nix::sys::memfd::{memfd_create, MemFdCreateFlag}; use raw_window_handle::{HasRawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle}; -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}; +use std::{ + ffi::CStr, + fs::File, + io::Write, + os::unix::prelude::{AsRawFd, FileExt, FromRawFd}, +}; +use wayland_client::{ + backend::{Backend, ObjectId}, + globals::{registry_queue_init, GlobalListContents}, + protocol::{wl_buffer, wl_registry, wl_shm, wl_shm_pool, wl_surface}, + Connection, Dispatch, EventQueue, Proxy, QueueHandle, +}; + +struct State; pub struct WaylandImpl { - _event_queue: EventQueue, - surface: WlSurface, - shm: Main, + event_queue: EventQueue, + qh: QueueHandle, + surface: wl_surface::WlSurface, + shm: wl_shm::WlShm, tempfile: File, - buffer: Option + buffer: Option, } -struct WaylandBuffer{ +struct WaylandBuffer { width: i32, height: i32, - buffer: Main + pool: wl_shm_pool::WlShmPool, + buffer: wl_buffer::WlBuffer, +} + +impl Drop for WaylandBuffer { + fn drop(&mut self) { + self.buffer.destroy(); + self.pool.destroy(); + } } impl WaylandImpl { - - pub unsafe fn new(window_handle: WaylandWindowHandle, display_handle: WaylandDisplayHandle) -> Result> { - let display = Display::from_external_display(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(window_handle.surface as _).into(); - Ok(Self{ - _event_queue: event_queue, - surface, shm, tempfile, - buffer: None + pub unsafe fn new( + window_handle: WaylandWindowHandle, + display_handle: WaylandDisplayHandle, + ) -> Result> { + let conn = Connection::from_backend(Backend::from_foreign_display( + display_handle.display as *mut _, + )); + let (globals, event_queue) = unwrap( + registry_queue_init(&conn), + "Failed to make round trip to server", + )?; + let qh = event_queue.handle(); + let shm: wl_shm::WlShm = unwrap( + globals.bind(&qh, 1..=1, ()), + "Failed to instantiate Wayland Shm", + )?; + let name = CStr::from_bytes_with_nul_unchecked("swbuf\0".as_bytes()); + let tempfile_fd = unwrap( + memfd_create(name, MemFdCreateFlag::MFD_CLOEXEC), + "Failed to create temporary file to store buffer.", + )?; + let tempfile = File::from_raw_fd(tempfile_fd); + let surface_id = unwrap( + ObjectId::from_ptr( + wl_surface::WlSurface::interface(), + window_handle.surface as _, + ), + "Failed to create proxy for surface ID.", + )?; + let surface = unwrap( + wl_surface::WlSurface::from_id(&conn, surface_id), + "Failed to create proxy for surface ID.", + )?; + Ok(Self { + event_queue: event_queue, + qh, + 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{ + 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, &self.qh, ()); + let buffer = pool.create_buffer( + 0, width, height, - buffer + width * 4, + wayland_client::protocol::wl_shm::Format::Xrgb8888, + &self.qh, + (), + ); + self.buffer = Some(WaylandBuffer { + width, + height, + pool, + buffer, }); } } - fn check_buffer_size_equals(&self, width: i32, height: i32) -> bool{ - match &self.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 + 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.tempfile.set_len(buffer.len() as u64 * 4) + .expect("Failed to truncate temporary file."); + 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); // FIXME: Proper damaging mechanism. // // In order to propagate changes on compositors which track damage, for now damage the entire surface. - if self.surface.as_ref().version() < 4 { + if self.surface.version() < 4 { // FIXME: Accommodate scale factor since wl_surface::damage is in terms of surface coordinates while // wl_surface::damage_buffer is in buffer coordinates. // @@ -76,9 +142,60 @@ impl GraphicsContextImpl for WaylandImpl { self.surface.damage(0, 0, i32::MAX, i32::MAX); } else { // Introduced in version 4, it is an error to use this request in version 3 or lower. - self.surface.damage_buffer(0, 0, width as i32, height as i32); + self.surface + .damage_buffer(0, 0, width as i32, height as i32); } self.surface.commit(); + let _ = self.event_queue.flush(); } -} \ No newline at end of file +} + +impl Dispatch for State { + fn event( + _: &mut State, + _: &wl_registry::WlRegistry, + _: wl_registry::Event, + _: &GlobalListContents, + _: &Connection, + _: &QueueHandle, + ) { + // Ignore globals added after initialization + } +} + +impl Dispatch for State { + fn event( + _: &mut State, + _: &wl_shm::WlShm, + _: wl_shm::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + _: &mut State, + _: &wl_shm_pool::WlShmPool, + _: wl_shm_pool::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for State { + fn event( + _: &mut State, + _: &wl_buffer::WlBuffer, + _: wl_buffer::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +}