From 4f6542ceaa8c2581aa7bbaa0cfe771fc3f5313a4 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 22 Dec 2022 10:13:32 -0800 Subject: [PATCH 1/2] Add support and CI tests for BSDs This adds a fallback using `shm_open`/`shm_unlink` for platforms where `memfd_create` doesn't exist. This seems to be how this is normally handled, though it's a bit ugly. This also builds the wayland/x11 code for NetBSD/OpenBSD/DragonFlyBSD. Add CI builds for FreeBSD and NetBSD. We would need some kind of virtualisation though to actually run tests on such targets. I've tested the `shm_open` logic on Linux, but haven't run it on any BSDs. --- .github/workflows/ci.yml | 12 ++++++-- Cargo.toml | 7 +++-- src/lib.rs | 59 +++++++++++++++++++++++++++++++++++----- src/wayland/buffer.rs | 50 ++++++++++++++++++++++++++++++---- 4 files changed, 112 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 671a5df..54c0137 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,8 @@ jobs: - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" } - { target: x86_64-unknown-redox, os: ubuntu-latest, } + - { target: x86_64-unknown-freebsd, os: ubuntu-latest, } + - { target: x86_64-unknown-netbsd, os: ubuntu-latest, } - { target: x86_64-apple-darwin, os: macos-latest, } # We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web # doesn't currently work on Linux. @@ -77,6 +79,8 @@ jobs: if: > !((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) && !contains(matrix.platform.target, 'redox') && + !contains(matrix.platform.target, 'freebsd') && + !contains(matrix.platform.target, 'netbsd') && matrix.rust_version != '1.60.0' run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES @@ -85,7 +89,9 @@ jobs: if: > !((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) && !contains(matrix.platform.target, 'wasm32') && - !contains(matrix.platform.target, 'redox') + !contains(matrix.platform.target, 'redox') && + !contains(matrix.platform.target, 'freebsd') && + !contains(matrix.platform.target, 'netbsd') run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES - name: Lint with clippy @@ -94,5 +100,7 @@ jobs: (matrix.rust_version == 'stable') && !contains(matrix.platform.options, '--no-default-features') && !((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) && - !contains(matrix.platform.target, 'redox') + !contains(matrix.platform.target, 'redox') && + !contains(matrix.platform.target, 'freebsd') && + !contains(matrix.platform.target, 'netbsd') run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings diff --git a/Cargo.toml b/Cargo.toml index ecf6bf7..43fac26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ rust-version = "1.60.0" [features] default = ["x11", "wayland", "wayland-dlopen"] -wayland = ["wayland-backend", "wayland-client", "nix"] +wayland = ["wayland-backend", "wayland-client", "nix", "fastrand"] wayland-dlopen = ["wayland-sys/dlopen"] x11 = ["bytemuck", "x11rb", "x11-dl"] @@ -23,7 +23,7 @@ thiserror = "1.0.30" raw-window-handle = "0.5.0" log = "0.4.17" -[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] +[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] nix = { version = "0.26.1", optional = true } wayland-backend = { version = "0.1.0", features = ["client_system"], optional = true } wayland-client = { version = "0.30.0", optional = true } @@ -32,6 +32,9 @@ bytemuck = { version = "1.12.3", optional = true } x11-dl = { version = "2.19.1", optional = true } x11rb = { version = "0.11.0", features = ["allow-unsafe-code", "dl-libxcb"], optional = true } +[target.'cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] +fastrand = { version = "1.8.0", optional = true } + [target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.42.0" features = ["Win32_Graphics_Gdi", "Win32_UI_WindowsAndMessaging", "Win32_Foundation"] diff --git a/src/lib.rs b/src/lib.rs index 39fbee6..9366ac9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,31 @@ extern crate core; mod cg; #[cfg(target_os = "redox")] mod orbital; -#[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd")))] +#[cfg(all( + feature = "wayland", + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" + ) +))] mod wayland; #[cfg(target_arch = "wasm32")] mod web; #[cfg(target_os = "windows")] mod win32; -#[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))] +#[cfg(all( + feature = "x11", + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" + ) +))] mod x11; mod error; @@ -66,9 +84,9 @@ macro_rules! make_dispatch { } make_dispatch! { - #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))] + #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))] X11(x11::X11Impl), - #[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd")))] + #[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))] Wayland(wayland::WaylandImpl), #[cfg(target_os = "windows")] Win32(win32::Win32Impl), @@ -105,21 +123,48 @@ impl GraphicsContext { raw_display_handle: RawDisplayHandle, ) -> Result { let imple: Dispatch = match (raw_window_handle, raw_display_handle) { - #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))] + #[cfg(all( + feature = "x11", + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" + ) + ))] ( RawWindowHandle::Xlib(xlib_window_handle), RawDisplayHandle::Xlib(xlib_display_handle), ) => Dispatch::X11(unsafe { x11::X11Impl::from_xlib(xlib_window_handle, xlib_display_handle)? }), - #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))] + #[cfg(all( + feature = "x11", + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" + ) + ))] ( RawWindowHandle::Xcb(xcb_window_handle), RawDisplayHandle::Xcb(xcb_display_handle), ) => Dispatch::X11(unsafe { x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle)? }), - #[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd")))] + #[cfg(all( + feature = "wayland", + any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" + ) + ))] ( RawWindowHandle::Wayland(wayland_window_handle), RawDisplayHandle::Wayland(wayland_display_handle), diff --git a/src/wayland/buffer.rs b/src/wayland/buffer.rs index c078d89..504206f 100644 --- a/src/wayland/buffer.rs +++ b/src/wayland/buffer.rs @@ -1,4 +1,3 @@ -use nix::sys::memfd::{memfd_create, MemFdCreateFlag}; use std::{ ffi::CStr, fs::File, @@ -15,6 +14,50 @@ use wayland_client::{ use super::State; +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +fn create_memfile() -> File { + use nix::sys::memfd::{memfd_create, MemFdCreateFlag}; + + let name = unsafe { CStr::from_bytes_with_nul_unchecked("softbuffer\0".as_bytes()) }; + let fd = memfd_create(name, MemFdCreateFlag::MFD_CLOEXEC) + .expect("Failed to create memfd to store buffer."); + unsafe { File::from_raw_fd(fd) } +} + +#[cfg(not(any(target_os = "linux", target_os = "freebsd")))] +fn create_memfile() -> File { + use nix::{ + errno::Errno, + fcntl::OFlag, + sys::{ + mman::{shm_open, shm_unlink}, + stat::Mode, + }, + }; + use std::iter; + + for _ in 0..=4 { + let mut name = String::from("softbuffer-"); + name.extend(iter::repeat_with(fastrand::alphanumeric).take(7)); + name.push('\0'); + + let name = unsafe { CStr::from_bytes_with_nul_unchecked(name.as_bytes()) }; + // `CLOEXEC` is implied with `shm_open` + let fd = shm_open( + name, + OFlag::O_RDWR | OFlag::O_CREAT | OFlag::O_EXCL, + Mode::S_IRWXU, + ); + if fd != Err(Errno::EEXIST) { + let fd = fd.expect("Failed to create POSIX shm to store buffer."); + let _ = shm_unlink(name); + return unsafe { File::from_raw_fd(fd) }; + } + } + + panic!("Failed to generate non-existant shm name") +} + pub(super) struct WaylandBuffer { qh: QueueHandle, tempfile: File, @@ -28,10 +71,7 @@ pub(super) struct WaylandBuffer { impl WaylandBuffer { pub fn new(shm: &wl_shm::WlShm, width: i32, height: i32, qh: &QueueHandle) -> Self { - let name = unsafe { CStr::from_bytes_with_nul_unchecked("softbuffer\0".as_bytes()) }; - let tempfile_fd = memfd_create(name, MemFdCreateFlag::MFD_CLOEXEC) - .expect("Failed to create memfd to store buffer."); - let tempfile = unsafe { File::from_raw_fd(tempfile_fd) }; + let tempfile = create_memfile(); let pool_size = width * height * 4; let pool = shm.create_pool(tempfile.as_raw_fd(), pool_size, qh, ()); let released = Arc::new(AtomicBool::new(true)); From a90c7bca0402fce671d49447a9245166c66de4c2 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 27 Dec 2022 15:17:42 -0800 Subject: [PATCH 2/2] Use `cfg_aliases` crate to make Wayland/X `#[cfg(..)]` less redundant --- Cargo.toml | 7 +++++-- build.rs | 7 +++++++ src/lib.rs | 59 +++++++----------------------------------------------- 3 files changed, 19 insertions(+), 54 deletions(-) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index 43fac26..380f9ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0.30" raw-window-handle = "0.5.0" log = "0.4.17" -[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] +[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies] nix = { version = "0.26.1", optional = true } wayland-backend = { version = "0.1.0", features = ["client_system"], optional = true } wayland-client = { version = "0.30.0", optional = true } @@ -32,7 +32,7 @@ bytemuck = { version = "1.12.3", optional = true } x11-dl = { version = "2.19.1", optional = true } x11rb = { version = "0.11.0", features = ["allow-unsafe-code", "dl-libxcb"], optional = true } -[target.'cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies] +[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox", target_os = "linux", target_os = "freebsd"))))'.dependencies] fastrand = { version = "1.8.0", optional = true } [target.'cfg(target_os = "windows")'.dependencies.windows-sys] @@ -55,6 +55,9 @@ features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElemen [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.3" +[build-dependencies] +cfg_aliases = "0.1.1" + [dev-dependencies] instant = "0.1.12" winit = "0.27.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..5113eeb --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +fn main() { + cfg_aliases::cfg_aliases! { + free_unix: { all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))) }, + x11_platform: { all(feature = "x11", free_unix, not(target_arch = "wasm32")) }, + wayland_platform: { all(feature = "wayland", free_unix, not(target_arch = "wasm32")) }, + } +} diff --git a/src/lib.rs b/src/lib.rs index 9366ac9..5e42408 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,31 +10,13 @@ extern crate core; mod cg; #[cfg(target_os = "redox")] mod orbital; -#[cfg(all( - feature = "wayland", - any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd", - target_os = "openbsd" - ) -))] +#[cfg(wayland_platform)] mod wayland; #[cfg(target_arch = "wasm32")] mod web; #[cfg(target_os = "windows")] mod win32; -#[cfg(all( - feature = "x11", - any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd", - target_os = "openbsd" - ) -))] +#[cfg(x11_platform)] mod x11; mod error; @@ -84,9 +66,9 @@ macro_rules! make_dispatch { } make_dispatch! { - #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))] + #[cfg(x11_platform)] X11(x11::X11Impl), - #[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))] + #[cfg(wayland_platform)] Wayland(wayland::WaylandImpl), #[cfg(target_os = "windows")] Win32(win32::Win32Impl), @@ -123,48 +105,21 @@ impl GraphicsContext { raw_display_handle: RawDisplayHandle, ) -> Result { let imple: Dispatch = match (raw_window_handle, raw_display_handle) { - #[cfg(all( - feature = "x11", - any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd", - target_os = "openbsd" - ) - ))] + #[cfg(x11_platform)] ( RawWindowHandle::Xlib(xlib_window_handle), RawDisplayHandle::Xlib(xlib_display_handle), ) => Dispatch::X11(unsafe { x11::X11Impl::from_xlib(xlib_window_handle, xlib_display_handle)? }), - #[cfg(all( - feature = "x11", - any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd", - target_os = "openbsd" - ) - ))] + #[cfg(x11_platform)] ( RawWindowHandle::Xcb(xcb_window_handle), RawDisplayHandle::Xcb(xcb_display_handle), ) => Dispatch::X11(unsafe { x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle)? }), - #[cfg(all( - feature = "wayland", - any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "netbsd", - target_os = "openbsd" - ) - ))] + #[cfg(wayland_platform)] ( RawWindowHandle::Wayland(wayland_window_handle), RawDisplayHandle::Wayland(wayland_display_handle),