From 06c7ec0f4dfd39a6d19a65bbcc7f0fcfe5b7039f Mon Sep 17 00:00:00 2001 From: jess Date: Sun, 26 Apr 2026 00:41:26 -0700 Subject: [PATCH] wayland - ARE YOU IN THERE? (im not angry, im cupping my hands like im speaking into a cave) --- src/bin/layers_shell.rs | 9 ++-- src/ffi.rs | 22 +++++++- src/wayland_shell.rs | 114 +++++++++++++++++++++++++++++++--------- 3 files changed, 117 insertions(+), 28 deletions(-) diff --git a/src/bin/layers_shell.rs b/src/bin/layers_shell.rs index 7637f12..d1b2f70 100644 --- a/src/bin/layers_shell.rs +++ b/src/bin/layers_shell.rs @@ -37,11 +37,14 @@ fn main() { #[cfg(all(target_os = "linux", feature = "wayland"))] { - layers::wayland_shell::run(); - return; + 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); diff --git a/src/ffi.rs b/src/ffi.rs index 2d35dc7..2e68671 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -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 { diff --git a/src/wayland_shell.rs b/src/wayland_shell.rs index ecfcdca..0918414 100644 --- a/src/wayland_shell.rs +++ b/src/wayland_shell.rs @@ -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 { + 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 = 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());