wayland - ARE YOU IN THERE? (im not angry, im cupping my hands like im speaking into a cave)

This commit is contained in:
jess 2026-04-26 00:41:26 -07:00
parent af5f80da2c
commit 06c7ec0f4d
3 changed files with 117 additions and 28 deletions

View File

@ -37,11 +37,14 @@ fn main() {
#[cfg(all(target_os = "linux", feature = "wayland"))]
{
layers::wayland_shell::run();
if layers::wayland_shell::try_run() {
return;
}
tracing::warn!(
"wayland provider failed to start; falling through to winit so the plugin still loads"
);
}
#[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);

View File

@ -427,7 +427,6 @@ fn finalise_handle(
let caps = surface.get_capabilities(&adapter);
let format = caps.formats.first().copied()?;
// PostMultiplied so the Metal layer's alpha blends against the transparent NSWindow.
let alpha_mode = if caps
.alpha_modes
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
@ -445,6 +444,27 @@ fn finalise_handle(
.unwrap_or(wgpu::CompositeAlphaMode::Auto)
};
let adapter_info = adapter.get_info();
tracing::info!(
backend = ?adapter_info.backend,
device_name = %adapter_info.name,
format = ?format,
all_formats = ?caps.formats,
all_alpha_modes = ?caps.alpha_modes,
chosen_alpha_mode = ?alpha_mode,
"wgpu surface configured"
);
if matches!(
alpha_mode,
wgpu::CompositeAlphaMode::Opaque | wgpu::CompositeAlphaMode::Auto
) {
tracing::warn!(
"wgpu chose {alpha_mode:?} — surface won't be transparent. \
X11 needs PreMultiplied/PostMultiplied. Try a different wgpu backend (set WGPU_BACKEND=gl or =vulkan) \
or check that the WM has a compositor running."
);
}
surface.configure(
&device,
&wgpu::SurfaceConfiguration {

View File

@ -33,19 +33,21 @@ const DEFAULT_LOGICAL_SIZE: (u32, u32) = (480, 640);
const MIN_LOGICAL_SIZE: (u32, u32) = (380, 220);
const ANCHOR_MARGIN: i32 = 24;
/// 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::error!("wayland: connect_to_env failed ({e}); aborting");
std::process::exit(1);
/// Run the Wayland (sctk + wlr-layer-shell) front end. Returns `true` if it took
/// over the session, `false` if init failed (wayland socket unreachable, no
/// layer-shell, etc.) so the caller can fall through to the winit path. Hard
/// exiting here would silently kill the plugin process inside KiCad — which the
/// user sees as "didn't load."
pub fn try_run() -> bool {
tracing::info!("wayland: try_run entered");
let conn = match probe_connection() {
Some(c) => c,
None => {
tracing::warn!(
"wayland: no reachable compositor socket; falling through to winit"
);
return false;
}
};
@ -55,8 +57,8 @@ pub fn run() {
pair
}
Err(e) => {
tracing::error!("wayland: registry init failed ({e}); aborting");
std::process::exit(1);
tracing::warn!("wayland: registry init failed ({e}); falling through to winit");
return false;
}
};
let qh = event_queue.handle();
@ -69,8 +71,8 @@ pub fn run() {
c
}
Err(e) => {
tracing::error!("wayland: wl_compositor missing ({e}); aborting");
std::process::exit(1);
tracing::warn!("wayland: wl_compositor missing ({e}); falling through to winit");
return false;
}
};
@ -80,10 +82,10 @@ pub fn run() {
l
}
Err(e) => {
tracing::error!(
"wayland: zwlr_layer_shell_v1 not advertised ({e}); compositor doesn't support layer-shell. Rebuild with --features x11."
tracing::warn!(
"wayland: zwlr_layer_shell_v1 not advertised ({e}); falling through to winit"
);
std::process::exit(1);
return false;
}
};
@ -112,8 +114,8 @@ pub fn run() {
match NonNull::new(conn.backend().display_ptr() as *mut _) {
Some(p) => p,
None => {
tracing::error!("wayland: null display ptr");
std::process::exit(1);
tracing::warn!("wayland: null display ptr; falling through to winit");
return false;
}
},
));
@ -121,8 +123,8 @@ pub fn run() {
match NonNull::new(layer.wl_surface().id().as_ptr() as *mut _) {
Some(p) => p,
None => {
tracing::error!("wayland: null surface ptr");
std::process::exit(1);
tracing::warn!("wayland: null surface ptr; falling through to winit");
return false;
}
},
));
@ -153,6 +155,7 @@ pub fn run() {
break;
}
}
true
}
struct State {
@ -491,6 +494,69 @@ impl PointerHandler for State {
}
}
/// Find a wayland socket and connect, regardless of whether `WAYLAND_DISPLAY` is set.
/// Inside flatpak the env var is often stripped even when the socket itself is bind-
/// mounted into the sandbox at `$XDG_RUNTIME_DIR/wayland-*`. We list candidates,
/// log everything we find, and try each in order.
fn probe_connection() -> Option<Connection> {
let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR").map(std::path::PathBuf::from);
tracing::info!(
xdg_runtime_dir = ?runtime_dir,
wayland_display_env = ?std::env::var_os("WAYLAND_DISPLAY"),
"wayland: probing for compositor socket"
);
if let Ok(c) = Connection::connect_to_env() {
tracing::info!("wayland: connected via $WAYLAND_DISPLAY");
return Some(c);
}
let Some(dir) = runtime_dir else {
tracing::warn!("wayland: $XDG_RUNTIME_DIR is unset; cannot scan for sockets");
return None;
};
let entries = match std::fs::read_dir(&dir) {
Ok(e) => e,
Err(e) => {
tracing::warn!(dir = %dir.display(), "wayland: cannot list runtime dir: {e}");
return None;
}
};
let mut candidates: Vec<String> = Vec::new();
for entry in entries.flatten() {
let name = entry.file_name();
let Some(name_str) = name.to_str() else { continue; };
if !name_str.starts_with("wayland-") {
continue;
}
// Skip lock files; we want the socket itself.
if name_str.ends_with(".lock") {
continue;
}
candidates.push(name_str.to_string());
}
candidates.sort();
tracing::info!(?candidates, "wayland: socket candidates in $XDG_RUNTIME_DIR");
for name in &candidates {
// Connection::connect_to_env reads $WAYLAND_DISPLAY; set it for one attempt.
std::env::set_var("WAYLAND_DISPLAY", name);
match Connection::connect_to_env() {
Ok(c) => {
tracing::info!(socket = name, "wayland: connected via probed socket");
return Some(c);
}
Err(e) => {
tracing::debug!(socket = name, "wayland: probe failed: {e}");
}
}
}
None
}
fn log_globals(globals: &GlobalList) {
let list = globals.contents().clone_list();
tracing::info!("wayland: registry advertises {} globals", list.len());