As kurt vile would say, "pretty cool"

This commit is contained in:
jess 2026-04-25 23:19:09 -07:00
parent 0cde65839c
commit af5f80da2c
5 changed files with 74 additions and 47 deletions

View File

@ -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 }

View File

@ -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:-<unset>}, WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-<unset>})"
cargo build --release --bin layers --no-default-features --features "$FEATURE"
cp "$ROOT/target/release/layers" "$APPDIR/Layers"
chmod +x "$APPDIR/Layers"

View File

@ -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");
layers::wayland_shell::run();
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)");
}
}
}
#[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")]

View File

@ -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;

View File

@ -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 {