From af5f80da2c00a2fc9ff003de952252ad53b84191 Mon Sep 17 00:00:00 2001 From: jess Date: Sat, 25 Apr 2026 23:19:09 -0700 Subject: [PATCH] As kurt vile would say, "pretty cool" --- Cargo.toml | 11 +++++++--- scripts/linux/build.sh | 26 ++++++++++++++++++++++- src/bin/layers_shell.rs | 47 +++++++++++++++++++++-------------------- src/lib.rs | 2 +- src/wayland_shell.rs | 35 ++++++++++++++---------------- 5 files changed, 74 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0bab2c0..79a98e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,12 @@ pretty_assertions = "1" tempfile = "3" [features] +default = ["x11"] debug = [] +# Linux front-end providers — exactly one selected at build time. +# build.sh detects the running WM and sets this for you. +x11 = ["dep:x11-dl"] +wayland = ["dep:smithay-client-toolkit", "dep:wayland-client"] [target.'cfg(target_os = "windows")'.dependencies] windows = { version = "0.58", features = [ @@ -80,6 +85,6 @@ windows = { version = "0.58", features = [ ] } [target.'cfg(target_os = "linux")'.dependencies] -x11-dl = "2" -smithay-client-toolkit = "0.20" -wayland-client = "0.31" +x11-dl = { version = "2", optional = true } +smithay-client-toolkit = { version = "0.20", optional = true } +wayland-client = { version = "0.31", optional = true } diff --git a/scripts/linux/build.sh b/scripts/linux/build.sh index efe4cd6..88cab15 100755 --- a/scripts/linux/build.sh +++ b/scripts/linux/build.sh @@ -30,7 +30,31 @@ if command -v rsvg-convert >/dev/null 2>&1 && [ -f "$ROOT/resources/Layers.svg" done fi -cargo build --release --bin layers +# Detect the running window-manager / display protocol to pick the front-end +# provider that gets compiled into the binary. Override with LAYERS_FEATURES. +detect_wm_feature() { + if [ -n "${LAYERS_FEATURES:-}" ]; then + echo "$LAYERS_FEATURES" + return + fi + # Native Wayland session AND a compositor known to ship wlr-layer-shell. + if [ -n "${WAYLAND_DISPLAY:-}" ]; then + case "${XDG_CURRENT_DESKTOP:-}" in + *COSMIC*|*KDE*|*Plasma*|sway|Hyprland|niri|river|Wayfire) + echo "wayland" + return + ;; + esac + fi + # GNOME-Wayland (mutter, no layer-shell), every X11 session, every other + # GTK3-class compositor: ship the X11 provider — XWayland honours the hints + # everywhere except cosmic-comp, where the wayland provider applies instead. + echo "x11" +} + +FEATURE="$(detect_wm_feature)" +echo "build: front-end provider = $FEATURE (XDG_CURRENT_DESKTOP=${XDG_CURRENT_DESKTOP:-}, WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-})" +cargo build --release --bin layers --no-default-features --features "$FEATURE" cp "$ROOT/target/release/layers" "$APPDIR/Layers" chmod +x "$APPDIR/Layers" diff --git a/src/bin/layers_shell.rs b/src/bin/layers_shell.rs index 771f080..7637f12 100644 --- a/src/bin/layers_shell.rs +++ b/src/bin/layers_shell.rs @@ -33,37 +33,32 @@ fn main() { ); log_session_env(); + tracing::info!(provider = PROVIDER_NAME, "shell route: provider chosen at build time"); - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "wayland"))] { - let wayland_display = std::env::var("WAYLAND_DISPLAY").ok(); - match wayland_display.as_deref() { - Some(name) => { - tracing::info!( - wayland_display = name, - "shell route: WAYLAND_DISPLAY set; trying sctk + wlr-layer-shell" - ); - if layers::wayland_shell::try_run() { - tracing::info!("shell route: wayland branch handled the session"); - return; - } - tracing::warn!( - "shell route: wayland branch declined (no layer-shell?); forcing WINIT_UNIX_BACKEND=x11 and falling through to winit" - ); - std::env::set_var("WINIT_UNIX_BACKEND", "x11"); - } - None => { - tracing::info!("shell route: no WAYLAND_DISPLAY; using winit directly (X11 path)"); - } - } + layers::wayland_shell::run(); + return; } + #[cfg(not(all(target_os = "linux", feature = "wayland")))] if let Err(e) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(run)) { tracing::error!("layers shell panicked: {e:?}"); std::process::exit(1); } } +#[cfg(all(target_os = "linux", feature = "wayland"))] +const PROVIDER_NAME: &str = "linux/wayland (sctk + wlr-layer-shell)"; +#[cfg(all(target_os = "linux", feature = "x11", not(feature = "wayland")))] +const PROVIDER_NAME: &str = "linux/x11 (winit + xlib)"; +#[cfg(all(target_os = "linux", not(feature = "x11"), not(feature = "wayland")))] +compile_error!("Linux build requires exactly one of: --features x11, --features wayland"); +#[cfg(target_os = "macos")] +const PROVIDER_NAME: &str = "macos (winit + appkit)"; +#[cfg(target_os = "windows")] +const PROVIDER_NAME: &str = "windows (winit + dwm)"; + fn log_window_kind(handle: &raw_window_handle::RawWindowHandle) -> &'static str { use raw_window_handle::RawWindowHandle::*; match handle { @@ -408,7 +403,7 @@ fn set_window_alpha(window: &Window, alpha: f32) { } } -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "x11"))] fn set_window_alpha(window: &Window, alpha: f32) { use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use std::ffi::CString; @@ -459,7 +454,13 @@ fn set_window_alpha(window: &Window, alpha: f32) { } } -#[cfg(not(any(target_os = "windows", target_os = "linux")))] +// macOS path: NSWindow.alphaValue is set in Swift; Rust side is a no-op. +// Linux/wayland builds bake alpha into render output (planned), not a window hint. +#[cfg(any( + target_os = "macos", + target_os = "ios", + all(target_os = "linux", not(feature = "x11")), +))] fn set_window_alpha(_window: &Window, _alpha: f32) {} #[cfg(target_os = "windows")] diff --git a/src/lib.rs b/src/lib.rs index 35cce6c..597e8db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,5 +14,5 @@ pub mod snapshot; pub mod ui; pub mod version; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "wayland"))] pub mod wayland_shell; diff --git a/src/wayland_shell.rs b/src/wayland_shell.rs index 4b31691..ecfcdca 100644 --- a/src/wayland_shell.rs +++ b/src/wayland_shell.rs @@ -33,20 +33,19 @@ const DEFAULT_LOGICAL_SIZE: (u32, u32) = (480, 640); const MIN_LOGICAL_SIZE: (u32, u32) = (380, 220); const ANCHOR_MARGIN: i32 = 24; -/// Try to run the Wayland (wlr-layer-shell) shell. Returns `true` if Wayland was used -/// — successfully started or not. Returns `false` only when no Wayland session was -/// available or the compositor doesn't advertise wlr-layer-shell, so the caller can -/// fall through to the winit/X11 path. -pub fn try_run() -> bool { - tracing::info!("wayland: try_run entered"); +/// Run the Wayland (sctk + wlr-layer-shell) front end. Compiled in only when the +/// `wayland` feature is enabled — the build script picks the provider based on the +/// running compositor. +pub fn run() { + tracing::info!("wayland: run entered"); let conn = match Connection::connect_to_env() { Ok(c) => { tracing::info!("wayland: Connection::connect_to_env OK"); c } Err(e) => { - tracing::info!("wayland: connect_to_env failed ({e}); falling through to X11"); - return false; + tracing::error!("wayland: connect_to_env failed ({e}); aborting"); + std::process::exit(1); } }; @@ -56,8 +55,8 @@ pub fn try_run() -> bool { pair } Err(e) => { - tracing::warn!("wayland: registry init failed ({e}); falling through to X11"); - return false; + tracing::error!("wayland: registry init failed ({e}); aborting"); + std::process::exit(1); } }; let qh = event_queue.handle(); @@ -70,8 +69,8 @@ pub fn try_run() -> bool { c } Err(e) => { - tracing::warn!("wayland: wl_compositor missing ({e}); falling through to X11"); - return false; + tracing::error!("wayland: wl_compositor missing ({e}); aborting"); + std::process::exit(1); } }; @@ -81,10 +80,10 @@ pub fn try_run() -> bool { l } Err(e) => { - tracing::warn!( - "wayland: zwlr_layer_shell_v1 not advertised ({e}; likely GNOME/mutter); falling through to X11" + tracing::error!( + "wayland: zwlr_layer_shell_v1 not advertised ({e}); compositor doesn't support layer-shell. Rebuild with --features x11." ); - return false; + std::process::exit(1); } }; @@ -114,7 +113,7 @@ pub fn try_run() -> bool { Some(p) => p, None => { tracing::error!("wayland: null display ptr"); - return true; + std::process::exit(1); } }, )); @@ -123,7 +122,7 @@ pub fn try_run() -> bool { Some(p) => p, None => { tracing::error!("wayland: null surface ptr"); - return true; + std::process::exit(1); } }, )); @@ -154,8 +153,6 @@ pub fn try_run() -> bool { break; } } - - true } struct State {