Skip to content

Commit d8087a6

Browse files
committed
Added Wayland support
1 parent d4dea5a commit d8087a6

File tree

5 files changed

+85
-2
lines changed

5 files changed

+85
-2
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ thiserror = "1.0.30"
1111
raw-window-handle = "0.4.2"
1212

1313
[target.'cfg(target_os = "linux")'.dependencies]
14+
tempfile = "3.3.0"
15+
wayland-client = {version = "0.29", features = ["use_system_lib"], default_features = false}
1416
x11-dl = "2.19.1"
1517

1618
[target.'cfg(target_os = "windows")'.dependencies]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ For now, the priority for new platforms is:
4242
- AppKit ✅ (Thanks to [Seo Sanghyeon](https://github.com/sanxiyn)!)
4343
- Orbital ❌
4444
- UiKit ❌
45-
- Wayland
45+
- Wayland ✅ (Wayland support in winit is immature at the moment, so it might be wise to force X11 if you're using winit)
4646
- Web ❌
4747
- Win32 ✅
4848
- WinRt ❌

src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,10 @@ pub enum SoftBufferError<W: HasRawWindowHandle> {
1515
#[error("Platform error")]
1616
PlatformError(Option<String>, Option<Box<dyn Error>>)
1717
}
18+
19+
pub(crate) fn unwrap<T, E: std::error::Error + 'static, W: HasRawWindowHandle>(res: Result<T, E>, str: &str) -> Result<T, SoftBufferError<W>>{
20+
match res{
21+
Ok(t) => Ok(t),
22+
Err(e) => Err(SoftBufferError::PlatformError(Some(str.into()), Some(Box::new(e))))
23+
}
24+
}

src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ mod win32;
1010
mod cg;
1111
#[cfg(target_os = "linux")]
1212
mod x11;
13+
#[cfg(target_os = "linux")]
14+
mod wayland;
1315

1416
mod error;
17+
1518
pub use error::SoftBufferError;
1619

1720
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
@@ -33,9 +36,11 @@ impl<W: HasRawWindowHandle> GraphicsContext<W> {
3336
/// - Ensure that the passed object is valid to draw a 2D buffer to
3437
pub unsafe fn new(window: W) -> Result<Self, SoftBufferError<W>> {
3538
let raw_handle = window.raw_window_handle();
36-
let imple = match raw_handle {
39+
let imple: Box<dyn GraphicsContextImpl> = match raw_handle {
3740
#[cfg(target_os = "linux")]
3841
RawWindowHandle::Xlib(xlib_handle) => Box::new(x11::X11Impl::new(xlib_handle)?),
42+
#[cfg(target_os = "linux")]
43+
RawWindowHandle::Wayland(wayland_handle) => Box::new(wayland::WaylandImpl::new(wayland_handle)?),
3944
#[cfg(target_os = "windows")]
4045
RawWindowHandle::Win32(win32_handle) => Box::new(win32::Win32Impl::new(&win32_handle)?),
4146
#[cfg(target_os = "macos")]

src/wayland.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use raw_window_handle::{HasRawWindowHandle, WaylandHandle};
2+
use tempfile::tempfile;
3+
use wayland_client::{Display, sys::client::wl_display, GlobalManager, protocol::{wl_shm::WlShm, wl_buffer::WlBuffer, wl_surface::WlSurface}, Main, Proxy, EventQueue};
4+
use crate::{GraphicsContextImpl, SoftBufferError, error::unwrap};
5+
use std::{fs::File, os::unix::prelude::{AsRawFd, FileExt}, io::Write};
6+
7+
pub struct WaylandImpl {
8+
_event_queue: EventQueue,
9+
surface: WlSurface,
10+
shm: Main<WlShm>,
11+
tempfile: File,
12+
buffer: Option<WaylandBuffer>
13+
}
14+
15+
struct WaylandBuffer{
16+
width: i32,
17+
height: i32,
18+
buffer: Main<WlBuffer>
19+
}
20+
21+
impl WaylandImpl {
22+
23+
pub unsafe fn new<W: HasRawWindowHandle>(handle: WaylandHandle) -> Result<Self, SoftBufferError<W>> {
24+
let display = Display::from_external_display(handle.display as *mut wl_display);
25+
let mut event_queue = display.create_event_queue();
26+
let attached_display = (*display).clone().attach(event_queue.token());
27+
let globals = GlobalManager::new(&attached_display);
28+
unwrap(event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()), "Failed to make round trip to server")?;
29+
let shm = unwrap(globals.instantiate_exact::<WlShm>(1), "Failed to instantiate Wayland Shm")?;
30+
let tempfile = unwrap(tempfile(), "Failed to create temporary file to store buffer.")?;
31+
let surface = Proxy::from_c_ptr(handle.surface as _).into();
32+
Ok(Self{
33+
_event_queue: event_queue,
34+
surface, shm, tempfile,
35+
buffer: None
36+
})
37+
}
38+
39+
fn ensure_buffer_size(&mut self, width: i32, height: i32){
40+
if !self.check_buffer_size_equals(width, height){
41+
let pool = self.shm.create_pool(self.tempfile.as_raw_fd(), width*height*4);
42+
let buffer = pool.create_buffer(0, width, height, width*4, wayland_client::protocol::wl_shm::Format::Xrgb8888);
43+
self.buffer = Some(WaylandBuffer{
44+
width,
45+
height,
46+
buffer
47+
});
48+
}
49+
}
50+
51+
fn check_buffer_size_equals(&self, width: i32, height: i32) -> bool{
52+
match &self.buffer{
53+
Some(buffer) => buffer.width == width && buffer.height == height,
54+
None => false
55+
}
56+
}
57+
58+
}
59+
60+
impl GraphicsContextImpl for WaylandImpl {
61+
unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) {
62+
self.ensure_buffer_size(width as i32, height as i32);
63+
let wayland_buffer = self.buffer.as_mut().unwrap();
64+
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.");
65+
self.tempfile.flush().expect("Failed to flush buffer to temporary file.");
66+
self.surface.attach(Some(&wayland_buffer.buffer), 0, 0);
67+
self.surface.commit();
68+
}
69+
}

0 commit comments

Comments
 (0)