Improvements to wayland backend
- Use wayland-client `0.30.0-beta.14` - Use `memfd` instead of temporary file - Make sure buffer and buffer pool objects are destroyed
This commit is contained in:
parent
7351d68814
commit
3dc11f75c1
2 changed files with 159 additions and 41 deletions
|
|
@ -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]
|
||||
|
|
|
|||
195
src/wayland.rs
195
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<WlShm>,
|
||||
event_queue: EventQueue<State>,
|
||||
qh: QueueHandle<State>,
|
||||
surface: wl_surface::WlSurface,
|
||||
shm: wl_shm::WlShm,
|
||||
tempfile: File,
|
||||
buffer: Option<WaylandBuffer>
|
||||
buffer: Option<WaylandBuffer>,
|
||||
}
|
||||
|
||||
struct WaylandBuffer{
|
||||
struct WaylandBuffer {
|
||||
width: i32,
|
||||
height: i32,
|
||||
buffer: Main<WlBuffer>
|
||||
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<W: HasRawWindowHandle>(window_handle: WaylandWindowHandle, display_handle: WaylandDisplayHandle) -> Result<Self, SoftBufferError<W>> {
|
||||
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::<WlShm>(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<W: HasRawWindowHandle>(
|
||||
window_handle: WaylandWindowHandle,
|
||||
display_handle: WaylandDisplayHandle,
|
||||
) -> Result<Self, SoftBufferError<W>> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
||||
fn event(
|
||||
_: &mut State,
|
||||
_: &wl_registry::WlRegistry,
|
||||
_: wl_registry::Event,
|
||||
_: &GlobalListContents,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<State>,
|
||||
) {
|
||||
// Ignore globals added after initialization
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_shm::WlShm, ()> for State {
|
||||
fn event(
|
||||
_: &mut State,
|
||||
_: &wl_shm::WlShm,
|
||||
_: wl_shm::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<State>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_shm_pool::WlShmPool, ()> for State {
|
||||
fn event(
|
||||
_: &mut State,
|
||||
_: &wl_shm_pool::WlShmPool,
|
||||
_: wl_shm_pool::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<State>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_buffer::WlBuffer, ()> for State {
|
||||
fn event(
|
||||
_: &mut State,
|
||||
_: &wl_buffer::WlBuffer,
|
||||
_: wl_buffer::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<State>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue